首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Asp.NET标识2提供“无效令牌”错误

Asp.NET标识2提供“无效令牌”错误
EN

Stack Overflow用户
提问于 2014-08-20 12:55:01
回答 23查看 83.2K关注 0票数 80

我正在使用Asp.Net-Identity-2,并试图使用以下方法验证电子邮件验证代码。但是我得到了一个“无效令牌”错误消息。

  • 我的应用程序的用户管理器如下: 公共类AppUserManager : UserManager { public AppUserManager(IUserStore store):base(IUserStore){} public static AppUserManager Create(IdentityFactoryOptions options,IOwinContext context) { AppIdentityDbContext db = context.Get();AppUserManager manager = new AppUserManager(新UserStore(db));manager.PasswordValidator =新PasswordValidator { RequiredLength = 6,RequireNonLetterOrDigit = false,RequireDigit = false,RequireLowercase = true,RequireUppercase = true };manager.UserValidator =新UserValidator(管理器){ AllowOnlyAlphanumericUserNames =真,RequireUniqueEmail =真};如果(dataProtectionProvider != null) { manager.UserTokenProvider =新的DataProtectorTokenProvider DataProtectorTokenProvider{ TokenLifespan = TimeSpan.FromHours(3) };} manager.EmailService =新EmailService(),则//令牌寿命为3小时;返回管理器;} //Create } //class }//命名空间
  • 生成令牌的操作是(即使我在这里检查令牌,也会得到“无效令牌”消息): AllowAnonymous ValidateAntiForgeryToken ValidateAntiForgeryToken public ActionResult ForgotPassword(string email) { if (ModelState.IsValid) { AppUser user = UserManager.FindByEmail(email);if (user == null UserManager.FindByEmail!(UserManager.IsEmailConfirmed(user.Id){ //没有警告任何错误.返回视图(“./Home/Index”);} //if字符串代码= UserManager.GeneratePasswordResetToken(user.Id);string callbackUrl = Url.Action("ResetPassword","Admin",new { Id = user.Id,code =HttpUtility.UrlEncode(代码) },协议: Request.Url.Scheme);UserManager.SendEmail(user.Id,“重置密码链接”,“使用以下链接重置密码: Link“);//我使用tho调试器提议的这2行。结果是:“无效令牌”(?)IdentityResult结果;结果= UserManager.ConfirmEmail(user.Id,代码);} //如果我们到了这一步,就会重新显示表单返回视图();}//忘记密码
  • 我检查令牌的操作是(在这里,我在检查结果时总是得到“无效令牌”): AllowAnonymous公共异步任务ResetPassword(string id,string代码){ if (id == null而外代码== null) {返回视图(“错误”,新的string[] {“重新设置密码的无效参数”);} IdentityResult结果;尝试{===等待UserManager.ConfirmEmailAsync( id,代码);} catch (InvalidOperationException物联网){ // ConfirmEmailAsync抛出当id未找到时。返回视图(“错误”,新string[] {“错误重置密码:”+ ioe.Message + "“});} if (result.Succeeded) { AppUser objUser =等待UserManager.FindByIdAsync(id);ResetPasswordModel模型=新ResetPasswordModel();model.Id = objUser.Id;model.Name = objUser.UserName;model.Email = objUser.Email;返回视图(模型);} //如果我们走到这一步,有些事情失败了。字符串strErrorMsg = "";foreach(string strError in result.Errors) { strErrorMsg +=“+ strError +”;} //foreach返回视图(“Error”,新string[] { strErrorMsg });}//忘记密码确认

我不知道会错过什么或者什么不对劲..。

EN

回答 23

Stack Overflow用户

回答已采纳

发布于 2014-08-20 13:28:46

因为您正在为密码重置生成令牌:

代码语言:javascript
复制
string code = UserManager.GeneratePasswordResetToken(user.Id);

但实际上试图验证电子邮件的令牌:

代码语言:javascript
复制
result = await UserManager.ConfirmEmailAsync(id, code);

这是两个不同的记号。

在您的问题中,您说您正在试图验证电子邮件,但您的代码是密码重置。你在做哪一个?

如果需要电子邮件确认,则通过以下方式生成令牌

代码语言:javascript
复制
var emailConfirmationCode = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);

并通过

代码语言:javascript
复制
var confirmResult = await UserManager.ConfirmEmailAsync(userId, code);

如果需要重置密码,请生成如下令牌:

代码语言:javascript
复制
var code = await UserManager.GeneratePasswordResetTokenAsync(user.Id);

并以如下方式确认:

代码语言:javascript
复制
var resetResult = await userManager.ResetPasswordAsync(user.Id, code, newPassword);
票数 74
EN

Stack Overflow用户

发布于 2015-03-11 10:17:33

我遇到了这个问题并解决了它。有几个可能的原因。

1. URL-编码问题(如果问题“随机”发生)

如果这种情况是随机发生的,您可能会遇到url编码问题。由于未知的原因,令牌不是为url安全设计的,这意味着它在通过url传递时可能包含无效字符(例如,通过电子邮件发送)。

在这种情况下,应该使用HttpUtility.UrlEncode(token)HttpUtility.UrlDecode(token)

正如o o Pereira在他的评论中所说的,UrlDecode不是(或者有时不是?)必填项。请两样都试试。谢谢。

2.非匹配方法(电子邮件与密码标记)

例如:

代码语言:javascript
复制
    var code = await userManager.GenerateEmailConfirmationTokenAsync(user.Id);

代码语言:javascript
复制
    var result = await userManager.ResetPasswordAsync(user.Id, code, newPassword);

电子邮件令牌提供程序生成的令牌不能由重置密码令牌提供程序确认.

但我们将看到这种情况发生的根本原因。

3.令牌提供者的不同实例

即使您正在使用:

代码语言:javascript
复制
var token = await _userManager.GeneratePasswordResetTokenAsync(user.Id);

随同

代码语言:javascript
复制
var result = await _userManager.ResetPasswordAsync(user.Id, HttpUtility.UrlDecode(token), newPassword);

这个错误仍有可能发生。

我的旧代码说明了为什么:

代码语言:javascript
复制
public class AccountController : Controller
{
    private readonly UserManager _userManager = UserManager.CreateUserManager(); 

    [AllowAnonymous]
    [HttpPost]
    public async Task<ActionResult> ForgotPassword(FormCollection collection)
    {
        var token = await _userManager.GeneratePasswordResetTokenAsync(user.Id);
        var callbackUrl = Url.Action("ResetPassword", "Account", new { area = "", UserId = user.Id, token = HttpUtility.UrlEncode(token) }, Request.Url.Scheme);

        Mail.Send(...);
    }

以及:

代码语言:javascript
复制
public class UserManager : UserManager<IdentityUser>
{
    private static readonly UserStore<IdentityUser> UserStore = new UserStore<IdentityUser>();
    private static readonly UserManager Instance = new UserManager();

    private UserManager()
        : base(UserStore)
    {
    }

    public static UserManager CreateUserManager()
    {
        var dataProtectionProvider = new DpapiDataProtectionProvider();
        Instance.UserTokenProvider = new DataProtectorTokenProvider<IdentityUser>(dataProtectionProvider.Create());

        return Instance;
    }

注意,在这段代码中,每次创建UserManager (或new-ed)时,都会生成一个新的dataProtectionProvider。因此,当用户收到电子邮件并单击链接时:

代码语言:javascript
复制
public class AccountController : Controller
{
    private readonly UserManager _userManager = UserManager.CreateUserManager();
    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> ResetPassword(string userId, string token, FormCollection collection)
    {
        var result = await _userManager.ResetPasswordAsync(user.Id, HttpUtility.UrlDecode(token), newPassword);
        if (result != IdentityResult.Success)
            return Content(result.Errors.Aggregate("", (current, error) => current + error + "\r\n"));
        return RedirectToAction("Login");
    }

AccountController不再是旧的,_userManager及其令牌提供程序也不再是旧的。因此,新的令牌提供程序将失败,因为它的内存中没有该令牌。

因此,我们需要为令牌提供程序使用一个实例。这是我的新代码,它运行得很好:

代码语言:javascript
复制
public class UserManager : UserManager<IdentityUser>
{
    private static readonly UserStore<IdentityUser> UserStore = new UserStore<IdentityUser>();
    private static readonly UserManager Instance = new UserManager();

    private UserManager()
        : base(UserStore)
    {
    }

    public static UserManager CreateUserManager()
    {
        //...
        Instance.UserTokenProvider = TokenProvider.Provider;

        return Instance;
    }

以及:

代码语言:javascript
复制
public static class TokenProvider
{
    [UsedImplicitly] private static DataProtectorTokenProvider<IdentityUser> _tokenProvider;

    public static DataProtectorTokenProvider<IdentityUser> Provider
    {
        get
        {

            if (_tokenProvider != null)
                return _tokenProvider;
            var dataProtectionProvider = new DpapiDataProtectionProvider();
            _tokenProvider = new DataProtectorTokenProvider<IdentityUser>(dataProtectionProvider.Create());
            return _tokenProvider;
        }
    }
}

它不能说是一个优雅的解决方案,但它触及了根源,解决了我的问题。

票数 106
EN

Stack Overflow用户

发布于 2015-04-28 17:45:19

即使使用这样的代码,我也会得到“无效令牌”错误:

代码语言:javascript
复制
var emailCode = UserManager.GenerateEmailConfirmationToken(id);
var result = UserManager.ConfirmEmail(id, emailCode);

在我的例子中,问题是,在不使用UserManager.Create(...) 方法的情况下,我手动创建用户并将其添加到数据库中。用户存在于数据库中,但没有安全标记。

有趣的是,GenerateEmailConfirmationToken返回一个令牌而不抱怨缺少安全标记,但该令牌永远无法验证。

票数 45
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/25405307

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档