Where to find C# sample code to implement password recovery in ASP .NET MVC2

后端 未结 5 1739
星月不相逢
星月不相逢 2020-12-05 01:28

How to implement password reset in MVC2 application?

Passwords are hashed using ASP .NET membership provider. Password recovery question is not used. Standard ASP .

相关标签:
5条回答
  • 2020-12-05 01:32

    Set a Reset password GUID in user table. You may also use an expiration time. If user tried to reset password, update the field with a new GUID and datetime for expiration.

    Send a link containing the link to reset password with the GUID.

    A sample function like this can be created for that

    GUID res = objPasswordResetService.resetPassword(Convert.ToInt64(objUserViewModel.UserID), restpasswordGuid, resetPasswordExpiryDateTime);
    

    The value in res can be the GUID updated in DB. Send a link with this GUID. You can check the expiration time also. This is just an idea only

    0 讨论(0)
  • 2020-12-05 01:42

    here the russian version password recovery

    0 讨论(0)
  • 2020-12-05 01:52

    Here is my approach. In MVC you will have an action called RetrievePassword where you will ask for the user's email address and pass it in a post

        [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);
        }
    

    An email message will be sent with a url that redirects to http://example.com/Account/Validate?email=xxxxxxxx&token=xxxxxxxx

    If the token is valid for the email, you will probably display a password reset form so they choose a new password.

    So you need a Validate Action:

    [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);
        }
    

    Here are some of the helpers you see in this code:

    Account Helper

    /// <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;
            }
        }
    

    And Finally here some code to encrypt, decript a token...

    I have it in a Password class that is intended to be a helper.

    /// EDIT: Removed the two functions I referenced before and show the full helper class.

    Here is the Password static class with all helper functions.

    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);
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-05 01:52

    I've got an example of how to implement password recovery in a standard ASP.NET MVC application in my blog.

    This blog post assumes that you already have the login process working (database and all) and that you only need to wire the password recovery process.

    http://hectorcorrea.com/Blog/Password-Recovery-in-an-ASP.NET-MVC-Project

    0 讨论(0)
  • 2020-12-05 01:52

    Answer to implement password reset in MVC2 application

    public string ResetPassword(string userName)
        {
            MembershipUser user = _provider.GetUser(userName, false);
    
            if (user.IsLockedOut)
                user.UnlockUser();
    
            user.Comment = null;
            _provider.UpdateUser(user);
    
            string newPassword = user.ResetPassword();
            string friendlyPassword = GenerateNewPassword();
            _provider.ChangePassword(userName, newPassword, friendlyPassword);
            return friendlyPassword;
        }
    
    
    private string GenerateNewPassword()
        {
            string strPwdchar = "abcdefghijklmnopqrstuvwxyz0123456789#@$ABCDEFGHIJKLMNOPQRSTUVWXYZ";
            string strPwd = "";
            Random rnd = new Random();
            for (int i = 0; i <= 8; i++)
            {
                int iRandom = rnd.Next(0, strPwdchar.Length - 1);
                strPwd += strPwdchar.Substring(iRandom, 1);
            }
            return strPwd;
        }
    
    0 讨论(0)
提交回复
热议问题