【问题标题】:"Invalid token" error while reset password, ASP.Net Identity 2.2.0重置密码时出现“无效令牌”错误,ASP.Net Identity 2.2.0
【发布时间】:2015-05-29 18:45:29
【问题描述】:

我正在开发一个 MVC5 ASP.Net 应用程序。
我正在使用 Identity 2.2.0 进行身份验证。
一切正常,但我无法重置密码,因为Invalid Token 错误。
以下是Account控制器中与ResetPassword相关的动作。

[AllowAnonymous]
public ActionResult ForgotPassword()
{
    return View();
}

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ForgotPassword(ForgotPasswordViewModel model)
{
    if (!ModelState.IsValid) return View(model);
    ApplicationUser userModel = await UserManager.FindByNameAsync(model.Email);

    if (userModel == null)
        ModelState.AddModelError("", "The user doesn't exist");

    if (userModel != null && !await UserManager.IsEmailConfirmedAsync(userModel.Id))
        ModelState.AddModelError("", "The user email isn't confirmed");

    if (!ModelState.IsValid) return View();

    var user = _userService.GetUserByEmail(model.Email);

    // For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=320771
    // Send an email with this link

    string websiteTitle = StaticAssests.WebsiteTitle;
    string emailContext = _settingService.GetValue(SettingNames.ResetPasswordMailFormat);
    string code = await UserManager.GeneratePasswordResetTokenAsync(userModel.Id);
    string callbackUrl = Url.Action("ResetPassword", "Account", new { userId = userModel.Id, code }, Request.Url.Scheme);
    emailContext = emailContext.Replace("{userfullname}", user.FullName);
    emailContext = emailContext.Replace("{websitetitle}", websiteTitle);
    emailContext = emailContext.Replace("{websitedomain}", StaticVariables.WebsiteDomain);
    emailContext = emailContext.Replace("{username}", userModel.UserName);
    emailContext = emailContext.Replace("{resetpasswordurl}", callbackUrl);
    emailContext = emailContext.Replace("{date}", new PersianDateTime(DateTime.Now).ToLongDateTimeString());
    await UserManager.SendEmailAsync(userModel.Id, string.Format("Reset password {0}", websiteTitle), emailContext);
    return RedirectToAction("ForgotPasswordConfirmation", "Account");
}

[AllowAnonymous]
public ActionResult ForgotPasswordConfirmation()
{
    return View();
}
[AllowAnonymous]
public ActionResult ResetPassword(string code)
{
    return code == null ? View("Error") : View();
}

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ResetPassword(ResetPasswordViewModel model)
{
    if (!ModelState.IsValid) return View(model);
    ApplicationUser userModel = await UserManager.FindByNameAsync(model.Email);
    if (userModel == null)
    {
        ModelState.AddModelError("", "The user doesn't exist");
        return View();
    }
// Invalid Token error
        IdentityResult result = await UserManager.ResetPasswordAsync(userModel.Id, model.Code, model.Password);
        if (result.Succeeded)
        {
            return RedirectToAction("ResetPasswordConfirmation", "Account");
        }
        AddErrors(result);
        return View();
    }

我已经检查了以下内容:
1. 重置邮件发送成功。
2.GeneratePasswordResetTokenAsync运行没有任何问题。并且生成的代码与ResetPassword动作中的code参数相同。

身份配置:

public class ApplicationUserManager : UserManager<ApplicationUser, int>
    {
        public ApplicationUserManager(IUserStore<ApplicationUser, int> userStore) : base(userStore)
        {

        }

        public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
        {
            ApplicationUserManager applicationUserManager = new ApplicationUserManager(new ApplicationUserStore());
            //ApplicationUserManager applicationUserManager = new ApplicationUserManager(context.Get<ApplicationUser>());
            //new ApplicationUserManager(new UserStore<UserModel>(context.Get<ApplicationDbContext>()));
            // Configure validation logic for usernames
            //applicationUserManager.UserValidator = new UserValidator<UserIdentityModel, int>(applicationUserManager)
            //{
            //  AllowOnlyAlphanumericUserNames = false,
            //  RequireUniqueEmail = true,
            //};
            applicationUserManager.PasswordValidator = new MyMinimumLengthValidator(6);
            applicationUserManager.UserValidator = new MyUserModelValidator();
            applicationUserManager.PasswordHasher = new MyPasswordHasher();

            // Configure user lockout defaults
            applicationUserManager.UserLockoutEnabledByDefault = true;
            applicationUserManager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
            applicationUserManager.MaxFailedAccessAttemptsBeforeLockout = 5;

            // Register two factor authentication providers. This application uses Phone and Emails as a step of receiving a code for verifying the user
            // You can write your own provider and plug in here.
            applicationUserManager.RegisterTwoFactorProvider("PhoneCode",
                new PhoneNumberTokenProvider<ApplicationUser, int>
                {
                    MessageFormat = "Your security code is: {0}"
                });
            applicationUserManager.RegisterTwoFactorProvider("EmailCode",
                new EmailTokenProvider<ApplicationUser, int>
                {
                    Subject = "Security code",
                    BodyFormat = "your security code is {0}"
                });

            applicationUserManager.EmailService = new EmailService();
            applicationUserManager.SmsService = new SmsService();
            IDataProtectionProvider dataProtectionProvider = options.DataProtectionProvider;
            if (dataProtectionProvider != null)
            {
                applicationUserManager.UserTokenProvider =
                    new DataProtectorTokenProvider<ApplicationUser, int>(dataProtectionProvider.Create("ASP.NET Identity"));
            }

            return applicationUserManager;
        }
    }

怎么了?

更新:
我已将 User Id 从字符串更改为 int。

【问题讨论】:

    标签: c# asp.net asp.net-mvc asp.net-identity-2


    【解决方案1】:

    我知道这是一个老问题,但我在两个项目中都遇到过这个问题,而且两次都是同样的问题。也许这个答案可能对其他人有所帮助。传递的令牌包含特殊字符,不能在 URL 字符串中按原样使用而不会导致问题。

    将令牌传递给前端 UI 时,一定要对令牌进行 URLEncode,如下所示:

    var callbackUrl =
                    new Uri(string.Format("{0}/resetpassword?userId={1}&code={2}",
                        ConfigurationManager.AppSettings["websiteUrl"], user.Id,
                        WebUtility.UrlEncode(token)));
    

    当令牌传回后端时,在将令牌传递给密码 ResetPassword 函数之前对令牌进行解码:

    var result = await this.AppUserManager.ResetPasswordAsync(appUser.Id, WebUtility.UrlDecode(resetPasswordBindingModel.ConfirmCode),
                 resetPasswordBindingModel.NewPassword);
    

    我遇到此问题的两个项目都在前端运行 HTML/MVC/JavaScript,并且验证是通过 ASP.NET WebAPI 2.x 完成的。

    另一个注意事项:由于某种原因,UrlDecode 无法正确解码加号,并且您在令牌中得到一个空格而不是“+”。我使用的技巧是仅使用字符串替换将任何空格转换为 + 符号。这并不理想,但我没有遇到任何问题。

    【讨论】:

    • 谢谢,我去测试一下。
    猜你喜欢
    • 1970-01-01
    • 2016-05-03
    • 2015-04-28
    • 2020-12-15
    • 2021-01-23
    • 2014-10-13
    • 2017-08-12
    • 2017-06-02
    • 2015-01-30
    相关资源
    最近更新 更多