Asp.net-Mvc

在哪裡可以找到在 ASP .NET MVC2 中實現密碼恢復的 C# 範常式式碼

  • May 18, 2013

如何在 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

如果令牌對電子郵件有效,您可能會顯示密碼重置表單,以便他們選擇新密碼。

所以你需要一個驗證動作:

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

引用自:https://stackoverflow.com/questions/7793108