asp.net-mvc - 哪里可以找到在 ASP .NET MVC2 中实现密码恢复的 C# 示例代码

标签 asp.net-mvc mono password-recovery

如何在MVC2应用程序中实现密码重置?

密码使用 ASP .NET 成员资格提供程序进行散列。不使用密码恢复问题。使用带有标准 AccountController 类的标准 ASP .NET MVC2 项目模板。

如果用户忘记密码,应将带有临时链接或新密码的电子邮件发送到用户电子邮件地址。

在哪里可以找到在 MVC 2 C# 中实现它的代码?

堆栈溢出包含两个讨论实现此方法的答案。没有示例代码。
我在谷歌上搜索了“asp .net mvc 密码重置 c# 示例代码下载”,但还没有找到示例代码。

我是 MVC 的新手。在哪里可以找到密码恢复的示例代码?这是 VS2010 生成的项目模板中缺少的。

更新

我在 Mono 2.10 中尝试了这段代码,但得到了异常:

Mono 不支持 CspParameters

在线

        des.Key = pdb.CryptDeriveKey("RC2", "MD5", 128, new byte[8]);

如何在 Mono 中运行它?

堆栈跟踪:
System.NotSupportedException: CspParameters not supported by Mono
at System.Security.Cryptography.PasswordDeriveBytes.CryptDeriveKey (string,string,int,byte[]) [0x0001b] in /usr/src/redhat/BUILD/mono-2.10.2/mcs/class/corlib/System.Security.Cryptography/PasswordDeriveBytes.cs:197
at store2.Helpers.Password.EncodeMessageWithPassword (string,string) <IL 0x00055, 0x000f3>
at store2.Helpers.AccountHelper.GetTokenForValidation (string) <IL 0x00033, 0x00089>
at MvcMusicStore.Controllers.AccountController.PasswordReminder (MvcMusicStore.Models.PasswordReminderModel) <IL 0x001ac, 0x00495>
at (wrapper dynamic-method) System.Runtime.CompilerServices.ExecutionScope.lambda_method (System.Runtime.CompilerServices.ExecutionScope,System.Web.Mvc.ControllerBase,object[]) <IL 0x00020, 0x0005b>
at System.Web.Mvc.ActionMethodDispatcher.Execute (System.Web.Mvc.ControllerBase,object[]) <IL 0x00008, 0x0001b>
at System.Web.Mvc.ReflectedActionDescriptor.Execute (System.Web.Mvc.ControllerContext,System.Collections.Generic.IDictionary`2<string, object>) <IL 0x00072, 0x00103>
at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod (System.Web.Mvc.ControllerContext,System.Web.Mvc.ActionDescriptor,System.Collections.Generic.IDictionary`2<string, object>) <IL 0x00003, 0x00019>
at System.Web.Mvc.ControllerActionInvoker/<>c__DisplayClassd.<InvokeActionMethodWithFilters>b__a () <IL 0x0002d, 0x00068>
at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter (System.Web.Mvc.IActionFilter,System.Web.Mvc.ActionExecutingContext,System.Func`1<System.Web.Mvc.ActionExecutedContext>) <IL 0x00031, 0x000b6>


--------------------------------------------------------------------------------
Version information: Mono Runtime Version: 2.10.2 (tarball Mon Apr 18 18:57:39 UTC 2011); ASP.NET Version: 2.0.50727.1433

最佳答案

这是我的方法。在 MVC 中,您将有一个名为 RetrievePassword 的操作,您将在其中询问用户的电子邮件地址并在帖子中传递它

    [HttpGet]
    public ActionResult RetrievePassword()
    {
        return View();
    }

    [HttpPost]
    public ActionResult RetrievePassword(PasswordRetrievalModel model)
    {
        if (ModelState.IsValid)
        {
            string username = Membership.GetUserNameByEmail(model.Email);

            if (!String.IsNullOrEmpty(username))
            {
                // This is a helper function that sends an email with a token (an MD5).
                NotificationsHelper.SendPasswordRetrieval(model.Email, this.ControllerContext);
            }
            else
            {
                Trace.WriteLine(String.Format("*** WARNING:  A user tried to retrieve their password but the email address used '{0}' does not exist in the database.", model.Email));
             }


            return RedirectToAction("Index", "Home");
        }

        return View(model);
    }

将发送带有重定向到 http://example.com/Account/Validate?email=xxxxxxxx&token=xxxxxxxx 的 URL 的电子邮件。

如果 token 对电子邮件有效,您可能会显示密码重置表单,以便他们选择新密码。

所以你需要一个验证操作:
[HttpGet]
    [CompressFilter]
    public ActionResult Validate(string email, string token)
    {
        bool isValid = false;

        if (AccountHelper.IsTokenValid(token, email))
        {
            string username = Membership.GetUserNameByEmail(email);
            if (!String.IsNullOrEmpty(username))
            {
                // Get the user and approve it.
                MembershipUser user = Membership.GetUser(username);
                user.IsApproved = true;
                Membership.UpdateUser(user);

                isValid = true;

                // Since it was a successful validation, authenticate the user.
                FormsAuthentication.SetAuthCookie(username, false);
            }
            else
            {
                isValid = false;
            }
        }

        return View(isValid);
    }

以下是您在此代码中看到的一些帮助程序:

账户助手
/// <summary>
    /// Gets the token for invitation.
    /// </summary>
    /// <param name="email">The email.</param>
    /// <returns></returns>
    public static string GetTokenForInvitation(string email)
    {
        if (String.IsNullOrEmpty(email))
            throw new ArgumentException("The email cannot be null");

        string token = Password.EncodeMessageWithPassword(String.Format("{0}#{1}", email, DateTime.Now), SEED);

        return token;
    }


    /// <summary>
    /// Gets the email from token.
    /// </summary>
    /// <param name="token">The token.</param>
    /// <param name="email">The email.</param>
    /// <returns></returns>
    public static bool GetEmailFromToken(string token, out string email)
    {
        email = String.Empty;


        string message = Password.DecodeMessageWithPassword(token, SEED);
        string[] messageParts = message.Split('#');

        if (messageParts.Count() != 2)
        {
            return false;
            // the token was not generated correctly.
        }
        else
        {
            email = messageParts[0];
            return true;
        }
    }



    /// <summary>
    /// Helper function used to generate a token to be used in the message sent to users when registered the first time to confirm their email address.
    /// </summary>
    /// <param name="email">The email address to encode.</param>
    /// <returns>The token generated from the email address, timestamp, and SEED value.</returns>
    public static string GetTokenForValidation(string email)
    {
        if (String.IsNullOrEmpty(email))
            throw new ArgumentException("The email cannot be null");

        string token = Password.EncodeMessageWithPassword(String.Format("{0}#{1}", email, DateTime.Now), SEED);

        return token;
    }


    /// <summary>
    /// Validates whether a given token is valid for a determined email address.
    /// </summary>
    /// <param name="token">The token to validate.</param>
    /// <param name="email">The email address to use in the validation.</param>
    /// <returns><c>true</c> if the token is valid, <c>false</c> otherwise.</returns>
    public static bool IsTokenValid(string token, string email)
    {
        return IsTokenValid(token, email, DateTime.Now);
    }


    /// <summary>
    /// Core method to validate a token that also offers a timestamp for testing.  In production mode should always be DateTime.Now.
    /// </summary>
    /// <param name="token">The token to validate.</param>
    /// <param name="email">the email address to use in the validation.</param>
    /// <param name="timestamp">The timestamp representing the time in which the validation is performed.</param>
    /// <returns><c>true</c> if the token is valid, <c>false</c> otherwise.</returns>
    public static bool IsTokenValid(string token, string email, DateTime timestamp)
    {
        if (String.IsNullOrEmpty(token))
            throw new ArgumentException("The token cannot be null");

        try
        {
            string message = Password.DecodeMessageWithPassword(token, SEED);
            string[] messageParts = message.Split('#');

            if (messageParts.Count() != 2)
            {
                return false;
                // the token was not generated correctly.
            }
            else
            {
                string messageEmail = messageParts[0];
                string messageDate = messageParts[1];

                // If the emails are the same and the date in which the token was created is no longer than 5 days, then it is valid. Otherwise, it is not. 
                return (String.Compare(email, messageEmail, true) == 0 && timestamp.Subtract(DateTime.Parse(messageDate)).Days < 5);
            }
        }
        catch (Exception)
        {
            // could not decrypt the message. The token has been tampered with.
            return false;
        }
    }

最后这里有一些代码来加密,解密 token ......

我有它在 密码 旨在成为帮助者的类。

///编辑:
删除了我之前引用的两个函数并显示完整的帮助器类。

这是带有所有辅助函数的 Password 静态类。
using System;
using System.Text;
using System.IO;
using System.Security.Cryptography;
using System.Data;
using System.Resources;

namespace MySolution.Common.Util
{
    /// <summary>
    /// Implements some functions to support password manipulation or generation
    /// </summary>
    public class Password
    {
        /// <summary>
        /// Takes a string and generates a hash value of 16 bytes.
        /// </summary>
        /// <param name="str">The string to be hashed</param>
        /// <param name="passwordFormat">Selects the hashing algorithm used. Accepted values are "sha1" and "md5".</param>
        /// <returns>A hex string of the hashed password.</returns>
        public static string EncodeString(string str, string passwordFormat)
        {
            if (str == null)
                return null;

            ASCIIEncoding AE = new ASCIIEncoding();
            byte[] result;
            switch (passwordFormat)
            {
                case "sha1":                    
                    SHA1 sha1 = new System.Security.Cryptography.SHA1CryptoServiceProvider();
                    result = sha1.ComputeHash(AE.GetBytes(str));
                    break;
                case "md5":
                    MD5 md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();
                    result = md5.ComputeHash(AE.GetBytes(str));
                    break;
                default:
                    throw new ArgumentException("Invalid format value. Accepted values are 'sha1' and 'md5'.", "passwordFormat");
            }

            // Loop through each byte of the hashed data 
            // and format each one as a hexadecimal string.
            StringBuilder sb = new StringBuilder(16);
            for (int i = 0; i < result.Length; i++)
            {
                sb.Append(result[i].ToString("x2"));
            }


            return sb.ToString();
        }

        /// <summary>
        /// Takes a string and generates a hash value of 16 bytes.  Uses "md5" by default.
        /// </summary>
        /// <param name="str">The string to be hashed</param>
        /// <returns>A hex string of the hashed password.</returns>
        public static string EncodeString(string str)
        {
            return EncodeString(str, "md5");
        }



        /// <summary>
        /// Takes a string and generates a hash value of 16 bytes.
        /// </summary>
        /// <param name="str">The string to be hashed</param>
        /// <param name="passwordFormat">Selects the hashing algorithm used. Accepted values are "sha1" and "md5".</param>
        /// <returns>A string of the hashed password.</returns>
        public static string EncodeBinary(byte[] buffer, string passwordFormat)
        {
            if (buffer == null)
                return null;

            byte[] result;
            switch (passwordFormat)
            {
                case "sha1":
                    SHA1 sha1 = new System.Security.Cryptography.SHA1CryptoServiceProvider();
                    result = sha1.ComputeHash(buffer);
                    break;
                case "md5":
                    MD5 md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();
                    result = md5.ComputeHash(buffer);
                    break;
                default:
                    throw new ArgumentException("Invalid format value. Accepted values are 'sha1' and 'md5'.", "passwordFormat");
            }


            // Loop through each byte of the hashed data 
            // and format each one as a hexadecimal string.
            StringBuilder sb = new StringBuilder(16);
            for (int i = 0; i < result.Length; i++)
            {
                sb.Append(result[i].ToString("x2"));
            }


            return sb.ToString();
        }

        /// <summary>
        /// Encodes the buffer using the default cryptographic provider.
        /// </summary>
        /// <param name="buffer">The buffer.</param>
        /// <returns></returns>
        public static string EncodeBinary(byte[] buffer)
        {
            return EncodeBinary(buffer, "md5");
        }





        /// <summary>
        /// Creates a random alphanumeric password.
        /// </summary>
        /// <returns>A default length character string with the new password.</returns>
        /// <remarks>The default length of the password is eight (8) characters.</remarks>
        public static string CreateRandomPassword()
        {
            //Default length is 8 characters
            return CreateRandomPassword(8);
        }

        /// <summary>
        /// Creates a random alphanumeric password on dimension (Length).
        /// </summary>
        /// <param name="Length">The number of characters in the password</param>
        /// <returns>The generated password</returns>
        public static string CreateRandomPassword(int Length)
        {
            Random rnd = new Random(Convert.ToInt32(DateTime.Now.Millisecond));  //Creates the seed from the time
            string Password="";
            while (Password.Length < Length ) 
            {
                char newChar = Convert.ToChar((int)((122 - 48 + 1) * rnd.NextDouble() + 48));
                if ((((int) newChar) >= ((int) 'A')) & (((int) newChar) <= ((int) 'Z')) | (((int) newChar) >= ((int) 'a')) & (((int) newChar) <= ((int) 'z')) | (((int) newChar) >= ((int) '0')) & (((int) newChar) <= ((int) '9')))
                    Password += newChar;
            }
            return Password;
        }

        /// <summary>
        /// Takes a text message and encrypts it using a password as a key.
        /// </summary>
        /// <param name="plainMessage">A text to encrypt.</param>
        /// <param name="password">The password to encrypt the message with.</param>
        /// <returns>Encrypted string.</returns>
        /// <remarks>This method uses TripleDES symmmectric encryption.</remarks>
        public static string EncodeMessageWithPassword(string plainMessage, string password)
        {
            if (plainMessage == null)
                throw new ArgumentNullException("encryptedMessage", "The message cannot be null");

            TripleDESCryptoServiceProvider des = new TripleDESCryptoServiceProvider();
            des.IV = new byte[8];

            //Creates the key based on the password and stores it in a byte array.
            PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, new byte[0]);
            des.Key = pdb.CryptDeriveKey("RC2", "MD5", 128, new byte[8]);

            MemoryStream ms = new MemoryStream(plainMessage.Length * 2);
            CryptoStream encStream = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write);
            byte[] plainBytes = Encoding.UTF8.GetBytes(plainMessage);
            encStream.Write(plainBytes, 0, plainBytes.Length);
            encStream.FlushFinalBlock();
            byte[] encryptedBytes = new byte[ms.Length];
            ms.Position = 0;
            ms.Read(encryptedBytes, 0, (int)ms.Length);
            encStream.Close();

            return Convert.ToBase64String(encryptedBytes);
        }

        /// <summary>
        /// Takes an encrypted message using TripleDES and a password as a key and converts it to the original text message.
        /// </summary>
        /// <param name="encryptedMessage">The encrypted message to decode.</param>
        /// <param name="password">The password to decode the message.</param>
        /// <returns>The Decrypted message</returns>
        /// <remarks>This method uses TripleDES symmmectric encryption.</remarks>
        public static string DecodeMessageWithPassword(string encryptedMessage, string password)
        {
            if (encryptedMessage == null)
                throw new ArgumentNullException("encryptedMessage", "The encrypted message cannot be null");

            TripleDESCryptoServiceProvider des = new TripleDESCryptoServiceProvider();
            des.IV = new byte[8];

            //Creates the key based on the password and stores it in a byte array.
            PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, new byte[0]);
            des.Key = pdb.CryptDeriveKey("RC2", "MD5", 128, new byte[8]);

            //This line protects the + signs that get replaced by spaces when the parameter is not urlencoded when sent.
            encryptedMessage = encryptedMessage.Replace(" ", "+");
            MemoryStream ms = new MemoryStream(encryptedMessage.Length * 2);
            CryptoStream decStream = new CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Write);

            byte[] plainBytes; 
            try 
            {
                byte[] encBytes = Convert.FromBase64String(Convert.ToString(encryptedMessage));
                decStream.Write(encBytes, 0, encBytes.Length);
                decStream.FlushFinalBlock();                
                plainBytes = new byte[ms.Length];
                ms.Position = 0;                
                ms.Read(plainBytes, 0, (int)ms.Length);
                decStream.Close();
            }
            catch(CryptographicException e)
            {
                throw new ApplicationException("Cannot decrypt message.  Possibly, the password is wrong", e);
            }

            return Encoding.UTF8.GetString(plainBytes);
        }
    }
}

关于asp.net-mvc - 哪里可以找到在 ASP .NET MVC2 中实现密码恢复的 C# 示例代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7793108/

相关文章:

c#文件路径中的非法字符

c# - 加载 JSON 数据失败

c# - 系统 namespace 在全新安装时缺少 MonoDevelop

linux - 如何恢复 Jenkins 密码

javascript - MVC - 处理新行的未终止字符串文字

asp.net-mvc - 根据 Html.TextBoxFor 的条件设置禁用属性

c# - 编辑时禁用用户名验证

linux - 我如何在 Linux/Mono 中使用 dockpanel 及其所有功能?

c# - 如何实现密码重置链接

algorithm - 是否有任何已知的算法、开源程序或白皮书可以在给定密码的已知部分的情况下破解密码?