我如何从ASP.NET Core中的密码重置令牌中检索用户?

问题描述 投票:0回答:2

我有一个ASP.NET Core 2.1 Web应用程序,并且正在添加忘记密码功能。我看了几个示例,它们似乎采用了两种方法之一。第一种方法是在密码重置URL中包含用户ID或用户的电子邮件以及密码重置令牌。第二种方法是在密码重置URL中仅包含密码重置令牌,然后要求用户在尝试更改密码(Binary Intellect example)时输入标识信息(例如电子邮件)。是否可以通过仅提供密码重置令牌来查找用户?

我的团队负责人已要求我仅在密码重置网址中传递令牌,然后查找用户。我的初步研究使我相信,我必须手动记录用户ID和令牌的关系,但是希望内置一些东西。我查看了ASP.NET Core UserManager documentation,但没有找到任何方法来检索用户给定的令牌。

这里有一些示例代码,将用户ID嵌入密码重置URL(Microsoft Password Recovery Doc):

var code = await _userManager.GeneratePasswordResetTokenAsync(user);
var callbackUrl = Url.Action("ResetPassword", "Account", new { userId = user.Id, code = code }, protocol: HttpContext.Request.Scheme);
c# asp.net-core asp.net-core-identity forgot-password
2个回答
1
投票

我相信您无法通过传递用户电子邮件然后在代码中寻找用户的方式来进行此操作

public async Task<IActionResult> ResetPassword([FromBody]ResetPasswordViewModel model)
{
    if (string.IsNullOrEmpty(model.Token) || string.IsNullOrEmpty(model.Email))
    {
        return RedirectToAction("Index", "Error", new { statusCode = AppStatusCode.NotFound });
    }

    var isResetTokenValid = await _userManager.CheckValidResetPasswordToken(model.Token, model.Email);

    if (!isResetTokenValid || string.IsNullOrEmpty(model.Email))
    {
        return StatusCode(AppStatusCode.ResetPassTokenExpire);
    }

    var user = await _userManager.FindByEmailAsync(model.Email);
    if (user == null)
    {
        return Ok();
    }

    await _userManager.ResetPasswordAsync(user, model.Token, model.Password);
    return Ok();
}

您可以查看实现细节here


0
投票

有一种方法可以从密码重置令牌中获取UserId,但我认为这很棘手,需要大量工作。

默认值是什么

如果您有类似以下的代码,

services.AddIdentity<AppUser, AppRole>(options =>
{
    ...
}
.AddEntityFrameworkStores<AppIdentityDbContext>()
.AddDefaultTokenProviders();

最后一行.AddDefaultTokenProviders() adds 4 default token providers,用于生成用于重置密码的令牌,更改电子邮件和更改电话号码选项,以及用于生成两因素身份验证令牌的令牌,进入管道:

  1. DataProtectorTokenProvider
  2. PhoneNumberTokenProvider
  3. EmailTokenProvider
  4. AuthenticatorTokenProvider

第一个是DataProtectorTokenProvider,这是我们想要的。它使用数据保护来序列化/加密那些令牌。

并且在DataProtectorTokenProvider中,其保护者为default to the name of "DataProtectorTokenProvider"

如何生成令牌

如果您查看GenerateAsync()中的GenerateAsync()方法,您可以告诉令牌由以下组成:

  • 令牌创建的统一时间戳(DataProtectorTokenProvider
  • DateTimeOffset.UtcNow
  • 目的字符串
  • 安全图章,如果支持的话

generate方法将所有这些连接起来,将它们转换为字节数组,然后调用内部的保护器来保护/加密有效负载。最终,有效负载将转换为基本64字符串。

如何获取用户ID

要从令牌中获取userId,您需要进行逆向工程:

  • 将令牌从基64字符串转换回字节数组
  • 调用内部保护程序以取消保护/解密字节数组
  • 读取Utc时间戳记
  • 读取userId

这里最棘手的部分是如何获得用于生成这些令牌的相同userId

如何获得默认的Data Protector

由于将默认的DataProtector输入到管道中,所以我想得到相同的DataProtectorTokenProvider的唯一方法是使用默认的DataProtector创建具有默认名称same的保护器, “ DataProtectorTokenProvider”,用于生成令牌!

DataProtectorTokenProvider

截屏:public class GetResetPasswordViewModelHandler : IRequestHandler<...> { ... private readonly IDataProtector _dataProtector; public GetResetPasswordViewModelHandler(..., IDataProtectionProvider dataProtectionProvider) { ... _dataProtector = dataProtectionProvider.CreateProtector("DataProtectorTokenProvider"); // OR // dataProtectionProvider.CreateProtector(new DataProtectionTokenProviderOptions().Name); } public async Task<ResetPasswordViewModel> Handle(GetResetPasswordViewModel query, ...) { // The password reset token comes from query.ResetToken var resetTokenArray = Convert.FromBase64String(query.ResetToken); var unprotectedResetTokenArray = _dataProtector.Unprotect(resetTokenArray); using (var ms = new MemoryStream(unprotectedResetTokenArray)) { using (var reader = new BinaryReader(ms)) { // Read off the creation UTC timestamp reader.ReadInt64(); // Then you can read the userId! var userId = reader.ReadString(); ... } } ... } }

我的2美分

[似乎只是尝试从密码重设令牌中读取enter image description here而已,这是很多工作。我了解您的团队负责人可能不想在密码重置链接上公开用户ID,或者他认为这是多余的,因为重置令牌具有userId

如果您使用整数表示userId,并且不想将其公开,则将其更改为userId

如果必须使用整数作为GUID,我将在用户个人资料之外创建一列unique_identifier类型的列(我将其称为PublicToken),并使用它来标识用户的所有公共事务。

userId
© www.soinside.com 2019 - 2024. All rights reserved.