我有这个代码用于在用户点击密码重置按钮时更改用户的密码(使用额外的代码登录ELMAH,这样我就可以尝试找出问题所在。)
这是在ASP.NET MVC 2中,使用标准的aspnet成员资格提供程序,使用这样的简单视图:
New Password: ______
Confirm Password: ______
[Reset] [Cancel]
此视图的路径为/Account/Reset/guid
,其中guid是aspnet成员资格数据库中的用户ID。
代码的关键部分是它调用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 :(得分:15)
如果用户需要重置密码,他们的帐户可能会被锁定,因为无效的尝试次数过多。如果是这种情况,则密码正在成功重置,但用户无法登录,直到锁定条件被清除。
尝试检查MembershipUser.IsLockedOut:
用户最常被锁定 无法通过验证 ValidateUser方法的时候 达到MaxInvalidPasswordAttempts 在PasswordAttemptWindow中。
将此属性设置为false并允许 用户尝试再次登录,即可 使用UnlockUser方法。
修改的
你还检查过IsApproved吗?验证将失败,对于用户来说这是false
。
另外,假设默认情况下成员资格提供程序是指SqlMembershipProvider,您是否可以对数据库运行以下查询并确保所有内容都正确无误?
select IsApproved, IsLockedOut, FailedPasswordAttemptCount
from aspnet_Membership
where ApplicationId = @yourApplicationId and UserId = @userId
在尝试登录以验证IsApproved
并且IsLockedOut
正常之前,请尝试执行查询。另请注意FailedPasswordAttemptCount
的值。
尝试登录,然后再次运行查询。如果登录失败,FailedPasswordAttemptCount
的值是否已递增?
您还可以查看aspnet_Membership表中的PasswordFormat
,并确保它是正确的值,具体取决于您使用的格式(0代表清除,1代表哈希,2代表加密)。
答案 1 :(得分:2)
bool MembershipUser.ChangePassword(string oldPassword, string newPassword)
我从未遇到过返回true且密码未正确更改的问题。 我可以告诉您的代码看起来不错。很难跟上,那里有所有的Elmah噪音。 (您可能希望将其删除或替换为简单的日志调用,以便更容易理解)。
验证作为参数传递的字符串id是否与目标用户的UserId相对应。您可能正在从其他用户发送userId并改为更改该用户密码。
答案 2 :(得分:1)
已编辑 - 以下答案为false,请参阅评论
等等,你是不是想通过Guid找人?通过做
Guid userId = new Guid(id);
您实际上创建的是唯一ID。所以我的猜测是你永远找不到用户而且你正在为没人成功重置密码。您是否可以通过传入的id参数找到它们?
答案 3 :(得分: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>
答案 4 :(得分:1)
我想知道问题是您在更改密码之前是否正在重置密码。如果没有进入Membership类的所有内部,你能尝试在这两个命令之间加入某种延迟吗?
答案 5 :(得分:1)
您使用的是哪个MemberShipProvider?每个用户都一样吗?例如,如果您使用SqlMembershipProvider并将enablePasswordReset设置为false,则它将悄然无法更新密码。在这种情况下,ChangePassword返回true,好像一切都很顺利。
答案 6 :(得分: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
正如您所看到的,update语句可能完全失败,并且存储过程可能返回true。我认为这是你的错误可能来自的地方。可能是锁定问题......
答案 7 :(得分:1)
这当然是一个有趣的问题。 “它适用于某些人,而不适用于其他人”部分非常奇怪。
这是一个间歇性问题,还是某些用户始终,而其他用户始终不会?
此处的其他人建议运行ValidateUser(username, newPassword)
以确认用户在成功之前可以正确进行身份验证。
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);
注意:这不适用于生产,只是用于测试是否可以解决问题。
答案 8 :(得分:1)
您使用的是1个网络服务器还是多个网络服务器?对于多个服务器,可能是用于加密密码的机器密钥在al服务器上是不同的。
答案 9 :(得分:0)
您的主要捕获块是否会抛出您未注意到的异常?
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。