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

C# 如何从ASP.NET Core中的密码重置令牌中检索用户?,c#,asp.net-core,asp.net-core-identity,forgot-password,C#,Asp.net Core,Asp.net Core Identity,Forgot Password,我有一个ASP.NET Core 2.1 web应用程序,正在添加忘记密码功能。我看了几个例子,它们似乎采取了两种方法中的一种。第一种方法是在密码重置url中包含用户id或用户电子邮件以及密码重置令牌。第二种方法是在密码重置url中仅包含密码重置令牌,然后要求用户在尝试更改密码()时输入标识信息(如电子邮件)。是否有一种方法可以查找仅给定密码重置令牌的用户 我的团队负责人要求我只传递密码重置url中的令牌,然后查找用户。我最初的研究让我相信,我必须手动记录用户id和令牌关系,但我希望有一些内置的

我有一个ASP.NET Core 2.1 web应用程序,正在添加忘记密码功能。我看了几个例子,它们似乎采取了两种方法中的一种。第一种方法是在密码重置url中包含用户id或用户电子邮件以及密码重置令牌。第二种方法是在密码重置url中仅包含密码重置令牌,然后要求用户在尝试更改密码()时输入标识信息(如电子邮件)。是否有一种方法可以查找仅给定密码重置令牌的用户

我的团队负责人要求我只传递密码重置url中的令牌,然后查找用户。我最初的研究让我相信,我必须手动记录用户id和令牌关系,但我希望有一些内置的东西。我已经查看了,但是没有找到任何方法来检索给定令牌的用户

下面是一些将用户id嵌入密码重置URL()的示例代码:


我相信你不可能做到,你可以通过用户电子邮件,然后找到它,在你的代码中寻找用户

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();
}
公共异步任务重置密码([FromBody]ResetPasswordViewModel)
{
if(string.IsNullOrEmpty(model.Token)| string.IsNullOrEmpty(model.Email))
{
返回RedirectToAction(“Index”,“Error”,new{statusCode=AppStatusCode.NotFound});
}
var isResetTokenValid=wait_userManager.CheckValidResetPasswordToken(model.Token,model.Email);
如果(!isResetTokenValid | | string.IsNullOrEmpty(model.Email))
{
返回状态码(AppStatusCode.ResetPassTokenExpire);
}
var user=await\u userManager.findbyemailsync(model.Email);
if(user==null)
{
返回Ok();
}
wait\u userManager.ResetPasswordAsync(用户、模型、令牌、模型、密码);
返回Ok();
}

您可以查看实现详细信息

有一种方法可以从密码重置令牌获取
用户ID
,但在我看来,这很棘手,而且工作量很大

默认设置是什么 如果您有如下代码

services.AddIdentity,用于生成用于重置密码、更改电子邮件和更改电话号码选项的令牌,以及用于生成双因素身份验证令牌的令牌

  • DataProtectorTokenProvider
  • PhoneNumberTokenProvider
  • EmailTokenProvider
  • 认证TortokenProvider
  • 第一个,DataProtectorTokenProvider,是我们正在寻找的。它使用数据保护来序列化/加密这些令牌

    DataProtectorTokenProvider
    中,其保护器是

    令牌是如何生成的 如果查看
    DataProtectorTokenProvider
    中的方法,可以看出令牌由以下部分组成:

    • 令牌创建的Utc时间戳(
      DateTimeOffset.UtcNow
    • userId
    • 目的字符串
    • 安全戳(如果支持)
    generate方法将所有这些连接起来,将它们转换为字节数组,并调用内部的保护器来保护/加密有效负载。最后,有效负载被转换为基本64字符串

    如何获取用户Id 要从令牌获取
    用户ID
    ,您需要执行反向工程:

    • 将令牌从基64字符串转换回字节数组
    • 调用内部的保护程序来取消对字节数组的保护/解密
    • 读取Utc时间戳
    • 读取
      userId
    这里棘手的部分是如何获得用于生成这些令牌的相同的
    DataProtector

    如何获取默认数据保护器 由于默认的
    DataProtectorTokenProvider
    已消失在管道中,因此我能想到的获得相同的
    DataProtector
    的唯一方法是使用默认的
    DataProtectorTokenProvider
    创建一个名为“DataProtectorTokenProvider”的默认保护器,用于生成令牌

    公共类GetResetPasswordViewModelHandler:IRequestHandler
    {
    ...
    专用只读IDataProtector_dataProtector;
    公共GetResetPasswordViewModelHandler(。。。,
    IDataProtectionProvider(数据保护提供程序)
    {
    ...
    _dataProtector=dataProtectionProvider.CreateProtector(“DataProtectorTokenProvider”);
    //或
    //dataProtectionProvider.CreateProtector(新的DataProtectionTokenProviderOptions().Name);
    }
    公共异步任务句柄(GetResetPasswordViewModel查询,…)
    {
    //密码重置令牌来自query.ResetToken
    var resetTokenArray=Convert.FromBase64String(query.ResetToken);
    var unprotectedResetTokenArray=\u dataProtector.Unprotect(resetTokenArray);
    使用(var ms=new MemoryStream(unprotectedResetTokenArray))
    {
    使用(变量读取器=新二进制读取器(ms))
    { 
    //读取创建UTC时间戳
    reader.ReadInt64();
    //然后您可以读取用户ID!
    var userId=reader.ReadString();
    ...
    }
    }
    ...
    }
    }
    
    屏幕截图:

    我的2美分 似乎要做很多工作,只要试着从密码重置令牌读取
    userId
    。我知道您的团队负责人可能不想在密码重置链接上公开用户id,或者他认为这是多余的,因为重置令牌具有
    用户id

    如果您使用integer来表示
    用户ID
    ,并且不想将其公开,我会将其更改为
    GUID

    如果您必须使用integer作为您的
    用户ID
    ,我只需在用户配置文件中创建一列类型为unique_identifier的列(我称之为PublicToken),并使用该列作为标识符
    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();
    }