DataProtectorTokenProvider 一次性配置

DataProtectorTokenProvider single use configuration

我正在尝试生成令牌以重置密码并使用 ASP .NET 将其发送到用户电子邮件。 但是,在验证之后,用户可以再次单击相同的 link,它仍然是 returns。我想配置为仅限一次性使用。 这是生成代码的地方

var code = await identityUserManager.GeneratePasswordResetTokenAsync(user.Id); //the code is generated here

这里是验证代码的地方

UserManager.VerifyUserToken(userId, "ResetPassword", code) //still return true after second click in Email

请帮帮我!非常感谢!

有两种方法可以解决这个问题:-

按照方法一,我们用token实现过期时间。在这里,用户仍然可以重复使用该代码,如果 过期时间超过要求。更好的解决办法是放一些RouteGuards,如方法2所示。这样做,不会允许用户第二次访问重设密码页面,without 生成新代码。因此,用户在这里只能使用一次代码。但是,那一次使用不会过期

所以,为了兼顾这两个好处,我建议同时实现这样用户将只能使用一次代码,并且在过期时间之前也是如此。

方法 1 -(为令牌实施过期时间)

创建一个包含 3 列(TokenCode、IssuedToEmailId、ExpireTime)的数据库来存储相同的内容,以 EmaildId 作为主键,这样一次就不会再向用户颁发一个令牌。在下面提供的 ForgetPassword() 中生成令牌,删除该用户 table 中的任何先前行,并插入新数据 -> 令牌代码、用户的电子邮件 ID 和到期时间,即,假设Datetime.Now.AddMinutes(10);

并且在下面提供的ResetPassword()中,使用电子邮件ID从table中检索数据,并比较Datetime.Now < ExpireTime如果为真,则继续,否则显示过期代码错误。

方法二-(实现路由守卫)

我的密码重置代码流程是,在忘记密码页面上,我只要求提供电子邮件地址,然后,让我们点击一​​个按钮开始令牌生成、电子邮件发送等过程。因此,在成功发送电子邮件后,我执行 return Ok(),然后在 sessionStorage 中启动会话并导航到重置密码页面。现在,一旦到了这里,我检查同一会话的可用性,如果没有找到,我知道它已被删除,我将用户导航到我想要的任何地方,但如果会话存在,我要求输入代码新密码,确认新密码,然后,假设单击按钮开始密码重置过程,然后在其 return Ok() 上,我删除了该会话。

因此,如果用户尝试通过访问重置密码 url 再次使用相同的令牌,他没有该会话,因此可以导航到我想要的任何位置。 这样我就不需要验证用户token了。

代码 =>

忘记密码:-

public async Task<IHttpActionResult> ForgotPassword(ForgotPasswordViewModel model)
    {
        if (ModelState.IsValid)
        {
            var user = await UserManager.FindByNameAsync(model.Email);
            // If user has to activate his email to confirm his account, the use code listing below
            //if (user == null || !(await UserManager.IsEmailConfirmedAsync(user.Id)))
            //{
            //    return Ok();
            //}
            if (user == null)
            {
                return NotFound();
            }

            // 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 code = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
            await UserManager.SendEmailAsync(user.Id, "Reset Password for AJWebApp", $"Please reset your password by using this => {code}");
            return Ok();
        }

        // If we got this far, something failed, redisplay form
        return BadRequest(ModelState);
    }

重置密码代码:-

public async Task<IHttpActionResult> ResetPassword(ResetPasswordViewModel model)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }
        var user = await UserManager.FindByNameAsync(model.Email);
        if (user == null)
        {
            // Don't reveal that the user does not exist
            return Ok();
        }
        var result = await UserManager.ResetPasswordAsync(user.Id, model.Code, model.Password);
        if (result.Succeeded)
        {
            return Ok();
        }
        return Ok();
    }