Asp.NET Identity 2给出"无效令牌"错误

 mobiledu2502897273 发布于 2022-12-27 05:18

我正在使用Asp.Net-Identity-2,我正在尝试使用以下方法验证电子邮件验证码.但我收到"无效令牌"错误消息.

我的应用程序的用户管理器是这样的:

public class AppUserManager : UserManager
{
    public AppUserManager(IUserStore store) : base(store) { }

    public static AppUserManager Create(IdentityFactoryOptions options, IOwinContext context)
    {
        AppIdentityDbContext db = context.Get();
        AppUserManager manager = new AppUserManager(new UserStore(db));

        manager.PasswordValidator = new PasswordValidator { 
            RequiredLength = 6,
            RequireNonLetterOrDigit = false,
            RequireDigit = false,
            RequireLowercase = true,
            RequireUppercase = true
        };

        manager.UserValidator = new UserValidator(manager)
        {
            AllowOnlyAlphanumericUserNames = true,
            RequireUniqueEmail = true
        };

        var dataProtectionProvider = options.DataProtectionProvider;

        //token life span is 3 hours
        if (dataProtectionProvider != null)
        {
            manager.UserTokenProvider =
               new DataProtectorTokenProvider
                  (dataProtectionProvider.Create("ConfirmationToken"))
               {
                   TokenLifespan = TimeSpan.FromHours(3)
               };
        }

        manager.EmailService = new EmailService();

        return manager;
    } //Create
  } //class
} //namespace

生成令牌的我的行动是(即使我在这里检查令牌,我得到"无效令牌"消息):

[AllowAnonymous]
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ForgotPassword(string email)
{
    if (ModelState.IsValid)
    {
        AppUser user = UserManager.FindByEmail(email);
        if (user == null || !(UserManager.IsEmailConfirmed(user.Id)))
        {
            // Returning without warning anything wrong...
            return View("../Home/Index");

        } //if

        string code = UserManager.GeneratePasswordResetToken(user.Id);
        string callbackUrl = Url.Action("ResetPassword", "Admin", new { Id = user.Id, code = HttpUtility.UrlEncode(code) }, protocol: Request.Url.Scheme);

        UserManager.SendEmail(user.Id, "Reset password Link", "Use the following  link to reset your password: link");

        //This 2 lines I use tho debugger propose. The result is: "Invalid token" (???)
        IdentityResult result;
        result = UserManager.ConfirmEmail(user.Id, code);
    }

    // If we got this far, something failed, redisplay form
    return View();

} //ForgotPassword

我检查令牌的行为是(在这里,当我检查结果时,我总是得到"无效令牌"):

[AllowAnonymous]
public async Task ResetPassword(string id, string code)
{

    if (id == null || code == null)
    {
        return View("Error", new string[] { "Invalid params to reset password." });
    }

    IdentityResult result;

    try
    {
        result = await UserManager.ConfirmEmailAsync(id, code);
    }
    catch (InvalidOperationException ioe)
    {
        // ConfirmEmailAsync throws when the id is not found.
        return View("Error", new string[] { "Error to reset password:

  • " + ioe.Message + "
  • " }); } if (result.Succeeded) { AppUser objUser = await UserManager.FindByIdAsync(id); ResetPasswordModel model = new ResetPasswordModel(); model.Id = objUser.Id; model.Name = objUser.UserName; model.Email = objUser.Email; return View(model); } // If we got this far, something failed. string strErrorMsg = ""; foreach(string strError in result.Errors) { strErrorMsg += "
  • " + strError + "
  • "; } //foreach return View("Error", new string[] { strErrorMsg }); } //ForgotPasswordConfirmation

    我不知道可能会遗漏什么或者什么是错的......

    6 个回答
    • 因为您在此处为密码重置生成令牌:

      string code = UserManager.GeneratePasswordResetToken(user.Id);
      

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

      result = await UserManager.ConfirmEmailAsync(id, code);
      

      这些是2种不同的代币.

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

      如果您需要确认电子邮件,请生成令牌

      var emailConfirmationCode = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
      

      并通过确认

      var confirmResult = await UserManager.ConfirmEmailAsync(userId, code);
      

      如果需要密码重置,请生成如下标记:

      var code = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
      

      并确认如下:

      var resetResult = await userManager.ResetPasswordAsync(user.Id, code, newPassword);
      

      2022-12-27 05:33 回答
    • 即使使用以下代码,我也会收到"无效令牌"错误:

      var emailCode = UserManager.GenerateEmailConfirmationToken(id);
      var result = UserManager.ConfirmEmail(id, emailCode);
      

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

      有趣的是,GenerateEmailConfirmationToken返回的令牌没有抱怨缺少安全标记,但该令牌永远无法验证.

      2022-12-27 05:40 回答
    • 除此之外,我已经看到代码本身如果没有编码就会失败.

      我最近开始以下列方式编码我的:

      string code = manager.GeneratePasswordResetToken(user.Id);
      code = HttpUtility.UrlEncode(code);
      

      然后当我准备好回读它时:

      string code = IdentityHelper.GetCodeFromRequest(Request);
      code = HttpUtility.UrlDecode(code);
      

      说实话,我很惊讶它首先没有被正确编码.

      2022-12-27 05:41 回答
    • 在我的例子中,我们的AngularJS应用程序将所有加号(+)转换为空格(""),因此令牌在传回时确实无效.

      要解决此问题,在AccountController中的ResetPassword方法中,我只是在更新密码之前添加了替换:

      code = code.Replace(" ", "+");
      IdentityResult result = await AppUserManager.ResetPasswordAsync(user.Id, code, newPassword);
      

      我希望这可以帮助其他任何在Web API和AngularJS中使用Identity的人.

      2022-12-27 05:46 回答
    • string code = _userManager.GeneratePasswordResetToken(user.Id);
      
                      code = HttpUtility.UrlEncode(code);
      

      //发送休息电子邮件


      不解码代码

      var result = await _userManager.ResetPasswordAsync(user.Id, model.Code, model.Password); 
      

      2022-12-27 05:50 回答
    • 我遇到了这个问题并解决了它.有几个可能的原因.

      1. URL编码问题(如果问题"随机发生")

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

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

      正如奥雷·佩雷拉在评论中所说,UrlDecode不是(有时不是?).请试试.谢谢.

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

      例如:

          var code = await userManager.GenerateEmailConfirmationTokenAsync(user.Id);
      

          var result = await userManager.ResetPasswordAsync(user.Id, code, newPassword);
      

      由reset-password-token-provider无法确认由email-token-provide生成的令牌.

      但我们会看到导致这种情况发生的根本原因.

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

      即使你使用:

      var token = await _userManager.GeneratePasswordResetTokenAsync(user.Id);
      

      随着

      var result = await _userManager.ResetPasswordAsync(user.Id, HttpUtility.UrlDecode(token), newPassword);
      

      错误仍然可能发生.

      我的旧代码说明了原因:

      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(...);
          }
      

      和:

      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都会生成新的.因此,当用户收到电子邮件并单击链接时:

      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它的令牌提供者.因此新的令牌提供程序将失败,因为它的内存中没有该令牌.

      因此,我们需要为令牌提供程序使用单个实例.这是我的新代码,它工作正常:

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

      和:

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

      它不能称为优雅的解决方案,但它触及了根本并解决了我的问题.

      2022-12-27 06:23 回答
    撰写答案
    今天,你开发时遇到什么问题呢?
    立即提问
    热门标签
    PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
    Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有