Где найти пример кода C # для реализации восстановления пароля в ASP .NET MVC2 - PullRequest
15 голосов
/ 17 октября 2011

Как реализовать сброс пароля в приложении MVC2?

Пароли хешируются с помощью поставщика членства ASP .NET. Вопрос восстановления пароля не используется. Используется стандартный шаблон проекта ASP .NET MVC2 со стандартным классом AccountController.

Если пользователь забыл пароль, на адрес электронной почты пользователя должно быть отправлено электронное письмо с временной ссылкой или новым паролем.

Где найти код для реализации этого в MVC 2 C #?

Переполнение стека

содержит два ответа, в которых обсуждаются методы реализации этого. Там нет образца кода. Я гуглил «asp .net mvc сброс пароля c # пример загрузки кода», но не нашел пример кода для этого.

Я новичок в MVC. Где найти пример кода для восстановления пароля? Это отсутствует в сгенерированном шаблоне проекта VS2010.

Обновление

Я попробовал этот код в Mono 2.10, но получил исключение:

CspParameters не поддерживаются Mono

в строке

        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

Ответы [ 5 ]

17 голосов
/ 17 октября 2011

Вот мой подход. В 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);
    }

Будет отправлено электронное сообщение с URL-адресом, перенаправляющим на http://example.com/Account/Validate?email=xxxxxxxx&token=xxxxxxxx

Если токен действителен для электронной почты, вы, вероятно, отобразите форму для сброса пароля, чтобы они выбрали новый пароль.

Итак, вам нужно проверить действие:

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

И, наконец, вот код для шифрования, расшифровки токена ...

У меня есть класс Password , который должен быть помощником.

/// РЕДАКТИРОВАТЬ: Удалены две функции, на которые я ссылался ранее, и показан полный вспомогательный класс.

Вот статический класс 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);
        }
    }
}
0 голосов
/ 18 мая 2013

здесь русская версия восстановление пароля

0 голосов
/ 03 декабря 2012

Ответ для восстановления пароля в приложении MVC2

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 голосов
/ 17 октября 2011

У меня есть пример того, как реализовать восстановление пароля в стандартном приложении ASP.NET MVC в моем блоге.

В этом посте предполагается, что у вас уже работает процесс входа в систему (база данных и все), и вам нужно только подключить процесс восстановления пароля.

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

0 голосов
/ 17 октября 2011

Установить GUID для сброса пароля в пользовательской таблице.Вы также можете использовать срок действия.Если пользователь попытался сбросить пароль, обновите поле, указав новый GUID и дату и время истечения срока действия.

Отправьте ссылку, содержащую ссылку для сброса пароля, с GUID.

Пример функции, подобной этой, можетбыть создан для этого

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

Значение в res может быть GUID, обновленным в БД.Отправьте ссылку с этим GUID.Вы также можете проверить срок годности.Это всего лишь идея

...