2011-02-19 37 views
12

我有這個代碼,當他們單擊密碼重置按鈕時(用額外的代碼登錄到ELMAH,以便我可以嘗試弄清楚發生了什麼問題)時更改用戶的密碼。ASP.NET成員身份更改密碼不起作用

這是在ASP.NET MVC 2,使用標準ASPNET會員供應商,用一個簡單的瀏覽是這樣的:

New Password:  ______ 
Confirm Password: ______ 
[Reset] [Cancel] 

這個觀點,這條路線是/Account/Reset/guid,其中GUID是用戶的ID在aspnet會員資料庫。

代碼的關鍵部分是它叫user.ChangePassword()。您可以看到它在成功時記錄了一條消息。問題是,對於某些用戶來說,成功消息被記錄下來,但他們無法使用新密碼登錄。對於其他用戶來說,它記錄了成功的消息,他們可以登錄

if (user.ChangePassword(pwd, confirmPassword)) 
{ 
    ErrorSignal.FromCurrentContext().Raise(
     new Exception("ResetPassword - changed successfully!")); 
    return Json(new { 
     Msg = "You have reset your password successfully." }, 
     JsonRequestBehavior.AllowGet); 
} 

完整的代碼清單:

[HttpPost] 
public JsonResult ResetPassword(string id, string newPassword, string confirmPassword) 
{ 
    ErrorSignal.FromCurrentContext().Raise(new Exception("ResetPassword started for " + id)); 

    ViewData["PasswordLength"] = Membership.MinRequiredPasswordLength; 

    if (string.IsNullOrWhiteSpace(newPassword)) 
    { 
     ErrorSignal.FromCurrentContext().Raise(
      new Exception("ResetPassword - new password was blank.")); 
     ModelState.AddModelError("_FORM", "Please enter a new password."); 
     return Json(new { Errors = ModelState.Errors() }, JsonRequestBehavior.AllowGet); 
    } 

    if (newPassword.Length < Membership.MinRequiredPasswordLength) 
    { 
     ErrorSignal.FromCurrentContext().Raise(
      new Exception("ResetPassword - new password was less than minimum length.")); 
     ModelState.AddModelError("_FORM", 
      string.Format("The password must be at least {0} characters long.", 
      Membership.MinRequiredPasswordLength)); 
     return Json(new { Errors = ModelState.Errors() }, JsonRequestBehavior.AllowGet); 
    } 

    if (string.IsNullOrWhiteSpace(confirmPassword)) 
    { 
     ErrorSignal.FromCurrentContext().Raise(
      new Exception("ResetPassword - confirm password was blank.")); 
     ModelState.AddModelError("_FORM", 
      "Please enter the same new password in the confirm password textbox."); 
     return Json(new { Errors = ModelState.Errors() }, JsonRequestBehavior.AllowGet); 
    } 

    if (confirmPassword.Length < Membership.MinRequiredPasswordLength) 
    { 
     ErrorSignal.FromCurrentContext().Raise(
      new Exception("ResetPassword - confirm password was less than minimum length.")); 
     ModelState.AddModelError("_FORM", 
      string.Format("The password must be at least {0} characters long.", 
      Membership.MinRequiredPasswordLength)); 
     return Json(new { Errors = ModelState.Errors() }, JsonRequestBehavior.AllowGet); 
    } 

    if (confirmPassword != newPassword) 
    { 
     ErrorSignal.FromCurrentContext().Raise(
      new Exception("ResetPassword - new password did not match the confirm password.")); 
     ModelState.AddModelError("_FORM", "Please enter the same password again."); 
     return Json(new { Errors = ModelState.Errors() }, JsonRequestBehavior.AllowGet); 
    } 

    bool isMatch = ValidationHelper.IsGUID(id); 
    if (string.IsNullOrWhiteSpace(id) || !isMatch) 
    { 
     ErrorSignal.FromCurrentContext().Raise(
      new Exception("ResetPassword - id was not a guid.")); 
     ModelState.AddModelError("_FORM", "An invalid ID value was passed in through the URL"); 
    } 
    else 
    { 
     //ID exists and is kosher, see if this user is already approved 
     //Get the ID sent in the querystring 
     Guid userId = new Guid(id); 

     try 
     { 
      //Get information about the user 
      MembershipUser user = Membership.GetUser(userId); 
      if (user == null) 
      { 
       //could not find the user 
       ErrorSignal.FromCurrentContext().Raise(
        new Exception("ResetPassword - could not find user by id " + id)); 
       ModelState.AddModelError("_FORM", 
        "The user account can not be found in the system."); 
      } 
      else 
      { 
       ErrorSignal.FromCurrentContext().Raise(
        new Exception("ResetPassword - user is " + user.UserName)); 
       string pwd = user.ResetPassword(); 

       if (user.ChangePassword(pwd, confirmPassword)) 
       { 
        ErrorSignal.FromCurrentContext().Raise(
         new Exception("ResetPassword - changed successfully!")); 
        return Json(new { 
         Msg = "You have reset your password successfully." }, 
         JsonRequestBehavior.AllowGet); 
       } 
       ErrorSignal.FromCurrentContext().Raise(
        new Exception("ResetPassword 
        - failed to change the password, for an unknown reason")); 
      } 
     } 
     catch (Exception ex) 
     { 
      ErrorSignal.FromCurrentContext().Raise(
       new Exception("ResetPassword: " + ex)); 
      return Json(new { Error = ex.Message + " -> " 
       + ex.InnerException.Message }, JsonRequestBehavior.AllowGet); 
     } 
    } 

    return Json(new { Errors = ModelState.Errors() }, JsonRequestBehavior.AllowGet); 
} 

編輯:添加賞金,試圖解決這項問題。這是我的問題列表中最令人討厭的問題之一,我不知道如何繼續。

+0

你親自親眼目睹過這件事嗎?你是否100%肯定用戶不只是忘記他們的密碼?此頁面上的密碼是否有尾隨/前導空格可能被剝離出登錄頁面? – Greg 2011-02-21 19:25:03

+0

此外,用戶在嘗試登錄時獲得的具體錯誤消息是什麼?在此期間是否還有其他重置密碼?基本上,MembershipProvider的工作原理與任何人都知道的一樣,並且您發佈的代碼看起來不錯,所以我的猜測是問題不是問題,或者是其他代碼。 – Greg 2011-02-21 19:32:10

+0

我已經嘗試通過將密碼重置爲「password1」,然後嘗試立即登錄。我無法登錄(用戶名或密碼不正確)。對於其他用戶和我自己的帳戶,我可以更改密碼。 – 2011-02-21 23:01:30

回答

15

如果用戶需要重置密碼,那麼他們的帳戶有可能被太多無效嘗試鎖定。如果是這種情況,則密碼將被成功復位,但用戶無法登錄,直到鎖定條件被清除。

嘗試檢查MembershipUser.IsLockedOut

用戶最常用的鎖定和 可以當達到 的PasswordAttemptWindow內 MaxInvalidPasswordAttempts不會被 的ValidateUser方法驗證。

要將此屬性設置爲false並讓 用戶再次嘗試登錄,您可以使用UnlockUser方法 。

編輯

你還要檢查IsApproved?身份驗證將失敗,這是用戶的false

此外,假設默認成員資格提供程序,您的意思是SqlMembershipProvider,您可以對您的數據庫運行以下查詢,並確保一切看起來正確?

select IsApproved, IsLockedOut, FailedPasswordAttemptCount 
from aspnet_Membership 
where ApplicationId = @yourApplicationId and UserId = @userId 

嘗試嘗試登錄驗證IsApprovedIsLockedOut都OK之前執行查詢。還請注意FailedPasswordAttemptCount的值。

嘗試登錄,然後再次運行查詢。如果登錄失敗,那麼FailedPasswordAttemptCount的值是否增加?

您也可以在aspnet_Membership表中查看PasswordFormat,並確定它是正確的值,具體取決於您使用的格式(清零,散列1,加密2)。

1

編輯 - 以下的答案是假的看評論

那麼等待你想找到一個GUID人嗎?通過這樣做

Guid userId = new Guid(id); 

你實際上是建立一個保證是唯一的ID。所以我猜你是從來沒有找到一個用戶,你沒有人成功地重置密碼。你能不能通過你傳入的id參數找到它們?

2

嗯,我一直使用

bool MembershipUser.ChangePassword(string oldPassword, string newPassword) 

我從來沒有一個問題,它返回true,密碼不正確更改。 就像我可以告訴你的代碼看起來確定一樣。這是很難跟隨,與所有的Elmah噪音在那裏。 (你可能想要刪除它或用一個簡單的日誌調用來替換,以便更容易遵循)。

驗證您作爲參數傳遞的字符串id是否與預期用戶的UserId相對應。您可能會從其他用戶發送userId並改爲更改該用戶密碼。

1

這個工作對我來說:

<%@ Page Title="Change Password" Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true" 
    CodeBehind="ChangePassword.aspx.cs" Inherits="WebPages.Account.ChangePassword" %> 

<asp:Content ID="HeaderContent" runat="server" ContentPlaceHolderID="HeadContent"> 
</asp:Content> 
<asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent"> 
    <h2> 
     Change Password 
    </h2> 
    <p> 
     Use the form below to change your password. 
    </p> 
    <p> 
     New passwords are required to be a minimum of <%= Membership.MinRequiredPasswordLength %> characters in length. 
    </p> 
    <asp:ChangePassword ID="ChangeUserPassword" runat="server" CancelDestinationPageUrl="~/" EnableViewState="false" RenderOuterTable="false" 
     OnChangedPassword="ChangeUserPassword_ChangedPassword"> 
     <ChangePasswordTemplate> 
      <span class="failureNotification"> 
       <asp:Literal ID="FailureText" runat="server"></asp:Literal> 
      </span> 
      <asp:ValidationSummary ID="ChangeUserPasswordValidationSummary" runat="server" CssClass="failureNotification" 
       ValidationGroup="ChangeUserPasswordValidationGroup"/> 
      <div class="accountInfo"> 
       <fieldset class="changePassword"> 
        <legend>Account Information</legend> 
        <p> 
         <asp:Label ID="CurrentPasswordLabel" runat="server" AssociatedControlID="CurrentPassword">Old Password:</asp:Label> 
         <asp:TextBox ID="CurrentPassword" runat="server" CssClass="passwordEntry" TextMode="Password"></asp:TextBox> 
         <asp:RequiredFieldValidator ID="CurrentPasswordRequired" runat="server" ControlToValidate="CurrentPassword" 
          CssClass="failureNotification" ErrorMessage="Password is required." ToolTip="Old Password is required." 
          ValidationGroup="ChangeUserPasswordValidationGroup">*</asp:RequiredFieldValidator> 
        </p> 
        <p> 
         <asp:Label ID="NewPasswordLabel" runat="server" AssociatedControlID="NewPassword">New Password:</asp:Label> 
         <asp:TextBox ID="NewPassword" runat="server" CssClass="passwordEntry" TextMode="Password"></asp:TextBox> 
         <asp:RequiredFieldValidator ID="NewPasswordRequired" runat="server" ControlToValidate="NewPassword" 
          CssClass="failureNotification" ErrorMessage="New Password is required." ToolTip="New Password is required." 
          ValidationGroup="ChangeUserPasswordValidationGroup">*</asp:RequiredFieldValidator> 
        </p> 
        <p> 
         <asp:Label ID="ConfirmNewPasswordLabel" runat="server" AssociatedControlID="ConfirmNewPassword">Confirm New Password:</asp:Label> 
         <asp:TextBox ID="ConfirmNewPassword" runat="server" CssClass="passwordEntry" TextMode="Password"></asp:TextBox> 
         <asp:RequiredFieldValidator ID="ConfirmNewPasswordRequired" runat="server" ControlToValidate="ConfirmNewPassword" 
          CssClass="failureNotification" Display="Dynamic" ErrorMessage="Confirm New Password is required." 
          ToolTip="Confirm New Password is required." ValidationGroup="ChangeUserPasswordValidationGroup">*</asp:RequiredFieldValidator> 
         <asp:CompareValidator ID="NewPasswordCompare" runat="server" ControlToCompare="NewPassword" ControlToValidate="ConfirmNewPassword" 
          CssClass="failureNotification" Display="Dynamic" ErrorMessage="The Confirm New Password must match the New Password entry." 
          ValidationGroup="ChangeUserPasswordValidationGroup">*</asp:CompareValidator> 
        </p> 
       </fieldset> 
       <p class="submitButton"> 
        <asp:Button ID="CancelPushButton" runat="server" CausesValidation="False" CommandName="Cancel" Text="Cancel"/> 
        <asp:Button ID="ChangePasswordPushButton" runat="server" CommandName="ChangePassword" Text="Change Password" 
         ValidationGroup="ChangeUserPasswordValidationGroup"/> 
       </p> 
      </div> 
     </ChangePasswordTemplate> 
     <SuccessTemplate> 
      <div class="accountInfo"> 
       <fieldset class="changePassword"> 
        <legend>Password changed</legend> 
         <p> 
          Your password has been changed. A confirmation e-mail has been sent to you. 
         </p> 
       </fieldset> 
      </div> 
     </SuccessTemplate> 
    </asp:ChangePassword> 
</asp:Content> 
1

我不知道這個問題是你改變它的權利之前,你重設密碼。沒有深入到Membership類的所有內部,你能否嘗試在這兩個命令之間進行某種延遲?

1

您使用的是哪一個MemberShipProvider?每個用戶都一樣嗎?例如,如果您使用SqlMembershipProvider並將enablePasswordReset設置爲false,則它將安靜地無法更新密碼。在這種情況下,ChangePassword返回true,就好像一切正​​常。

0

您的主要catch塊是否會拋出異常本身,你沒有注意到?

catch (Exception ex) 
{ 
    ErrorSignal.FromCurrentContext().Raise(new Exception("ResetPassword: " + ex)); 
    return Json(new { Error = ex.Message + " -> " 
      + ex.InnerException.Message }, JsonRequestBehavior.AllowGet); 
} 

ex.InnerException.Message語句不安全,因爲它可能會引發NullReferenceException。

1

如果您使用內置的基於SQLServer的提供程序,請查看您的SQL存儲過程。這是我的默認proc看起來像:

ALTER PROCEDURE dbo.aspnet_Membership_SetPassword 
    @ApplicationName nvarchar(256), 
    @UserName   nvarchar(256), 
    @NewPassword  nvarchar(128), 
    @PasswordSalt  nvarchar(128), 
    @CurrentTimeUtc datetime, 
    @PasswordFormat int = 0 
AS 
BEGIN 
    DECLARE @UserId uniqueidentifier 
    SELECT @UserId = NULL 
    SELECT @UserId = u.UserId 
    FROM dbo.aspnet_Users u, dbo.aspnet_Applications a, dbo.aspnet_Membership m 
    WHERE LoweredUserName = LOWER(@UserName) AND 
      u.ApplicationId = a.ApplicationId AND 
      LOWER(@ApplicationName) = a.LoweredApplicationName AND 
      u.UserId = m.UserId 

    IF (@UserId IS NULL) 
     RETURN(1) 

    UPDATE dbo.aspnet_Membership 
    SET Password = @NewPassword, PasswordFormat = @PasswordFormat, PasswordSalt = @PasswordSalt, 
     LastPasswordChangedDate = @CurrentTimeUtc 
    WHERE @UserId = UserId 
    RETURN(0) 
END 

正如你所看到的更新聲明可能完全失敗,存儲的過程可能會返回true。我認爲這是你的錯誤可能來自哪裏。可能會鎖定問題...

1

嗯,這當然是一個有趣的問題。 「它適用於某些人,而不是其他人」部分真的很怪異。

這是一個間歇性問題,或者它總是發生對某些用戶和永遠不會發生爲其他用戶?

這裏的其他人之一建議運行ValidateUser(username, newPassword)以確認用戶在假定成功之前可以正確進行身份驗證。

你試過這個嗎?您可以連續循環,重置+更改密碼,直到ValidateUser成功,也許在N次故障後退出。

bool success = false; 
int numAttempts = 0; 
do 
{ 
    string pwd = user.ResetPassword(); 
    if (user.ChangePassword(pwd, confirmPassword)) 
    { 
     success = Membership.ValidateUser(user.UserName, pwd); 
    } 
    numAttempts++; 
} while(numAttempts < 5 && !success); 

注:這不是在生產中使用,只是爲了測試,看看是否能解決這個問題。

1

您是使用1個網絡服務器還是多個網絡服務器?對於多臺服務器,可能是用於加密密碼的機器密鑰在al服務器上不一樣。