如何在MVC2应用程序中实现密码重置?
@H_301_30@解决方法
使用ASP .NET成员资格提供程序对密码进行散列.未使用密码恢复问题.使用标准ASP.NET MVC2项目模板与标准的AccountController类.
如果用户原谅密码,具有临时链接或新密码的电子邮件应发送给用户的电子邮件地址.
在哪里可以找到在MVC 2 C#中实现的代码?
堆栈溢出包含两个答案,讨论有关实现此方法的方法.没有示例代码.
我googled为“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,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的网址
如果令牌对电子邮件有效,您可能会显示密码重置表单,以便选择新密码.
所以你需要一个验证操作:
[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}",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,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,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; } }
最后这里有一些加密代码,一个令牌,
我有一个密码类,这是一个帮助者.
///编辑:
删除了之前引用的两个函数,并显示完整的helper类.
这是具有所有帮助函数的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",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,plainBytes.Length); encStream.FlushFinalBlock(); byte[] encryptedBytes = new byte[ms.Length]; ms.Position = 0; ms.Read(encryptedBytes,(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[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,encBytes.Length); decStream.FlushFinalBlock(); plainBytes = new byte[ms.Length]; ms.Position = 0; ms.Read(plainBytes,(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); } } }