ASP.NET身份验证重设密码时令牌无效。

24

我在我的MVC应用程序中通过复制VS 2013模板中的代码来实现了ASP.NET身份验证。基本功能可以使用,但是我无法使重置密码功能正常工作。当我显示“忘记密码”页面时,会生成一个包含令牌的电子邮件。该令牌由以下方法返回:

UserManager.GeneratePasswordResetTokenAsync(user.Id)
当我点击链接时,重置密码表单会打开,并允许用户输入他们的电子邮件地址和新密码。然后调用更改密码功能:
UserManager.ResetPasswordAsync(user.Id, model.Code, model.Password);

我认为这看起来不错,但结果总是显示“无效令牌”,我不知道原因。

有人知道为什么它不起作用吗?这个令牌到底存储在哪里?我以为它必须在数据库中的AspNetUsers表中...

5个回答

46

ASP.NET Identity中由UserManager生成的令牌通常包含"+"字符,当作为查询字符串传递时,URL中的"+"会被转换为""(一个空格)。在你的ResetPassword ActionResult中将""替换为"+",如下所示:

var code = model.Code.Replace(" ", "+");
//And then change the following line 
UserManager.ResetPasswordAsync(user.Id, model.Code, model.Password);
//To this one so it uses the code(spaces replaced with "+") instead of model.Code
UserManager.ResetPasswordAsync(user.Id, code, model.Password);

这应该就可以解决问题了。我曾经遇到同样的问题,并在这里找到了答案。


3
当其他回答中描述的UrlEncode/Decode无法正常工作时,对于我来说,这种方法是有效的。 - Adrian Carr
我也遇到了同样的问题,我的代码最后一行无法获取'==',那么我该如何处理呢?我通过查询字符串将代码发送到邮件中,并尝试在邮件发送时使用WebUtility.UrlEncode(code),并在获取时使用WebUtility.UrDecode(Model.code),但仍然遇到了问题,最终得到了无效的标记。 - coderwill
这个节省了我两倍的时间。 - Almeida
@Mansoor,你对我的这篇帖子有什么建议吗?我尝试了你上面的建议,但它没有起作用。 - nam
真的很好奇为什么解码对我也不起作用。这个答案确实有效,并且让我省了很多麻烦,因为令牌并不总是生成带有那个符号的。 - JoeCo

34

我想补充一点,除了HTML编码/解码之外,最常见的问题是数据库中的用户输入可能缺少安全戳(SecurityStamp)。在ASP.NET身份验证中存在一个错误,其中一个函数在创建令牌时将其设置为null,而另一个验证令牌时检查是否为空字符串。

如果您的SecurityStamp为null或空字符串,则会导致令牌无效的问题。


1
这也是我的问题。我只是用一个随机字符串更新了空值,它似乎对此感到满意!大量的咒骂和愤怒得以缓解。我欠你一个人情。 - Shawson
谢谢,这解决了我的问题,由于我们的大多数用户添加了SecurityToken,所以问题似乎是相同的,但是对于新用户添加时呢?他们最初会被赋予有效的安全令牌吗? - Ryan
3
这个解决了我的问题 - 我只是运行了UPDATE [AspNetUsers] SET [SecurityStamp] = NEWID() WHERE [SecurityStamp] IS NULL来填补空值,然后就正常工作了。 - Jammer
但是我们如何防止这种情况再次发生? - barnacle.m
null SecurityStamp 是我的问题。谢谢! - Elnoor

2

对我来说,安全戳(security stamp)没问题。与接受的答案一致,我使用编码方法来编码附带重置链接的代码,使用HttpContext.Current.Server.UrlEncode,就像这样:

string code = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
string callbackUrl = ConfigurationManager.AppSettings["baseurl"] + "/resetpassword?email=" + user.Email + "&code=" + HttpContext.Current.Server.UrlEncode(code);

2
如果您生成令牌后SecurityStamp发生更改,则该令牌也无效。
例如,您使用以下方式生成令牌:
UserManager.GeneratePasswordResetTokenAsync(user.Id);

之后调用。
UserManager.RemovePasswordAsync(user.Id);

您的SecurityStamp已经得到了更新,因此令牌现在已经失效

当我在调用GeneratePasswordResetTokenAsync之前添加RemovePasswordAsync时,它对我起作用了。 - undefined

0
在我的情况下,这是因为数据库中的数据从另一个数据库错误地导入。`SecurityStamp`字段为空,所以出现了无效令牌错误。

移除了特定表格的数据导入功能,改为使用UserManager从旧数据库创建用户。 - Afshar Mohebi
@TylerJamesHarden 我不记得了,也许我在阅读你的回答之前就发布了我的回答。顺便说一下,对于任何不便我很抱歉。 - Afshar Mohebi
我曾经遇到过这个问题,为了创建正确的有效令牌,我在用户的安全戳字段中放置了新的GUID。一旦我这样做了,它就开始工作了。 - MattjeS

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接