AspnetIdentity在哪里存储UserTokens?

时间:2016-07-30 05:17:39

标签: asp.net-identity identity asp.net-identity-2

我有ASP.Net MVC5应用程序。当您选择个人身份验证时创建应用程序Visual Studio使用脚手架并添加登录,创建用户,忘记密码等代码。我正在使用ForgotPassword功能的默认实现。这就是忘记密码的工作原理。

1>用户点击忘记密码链接。

2 - ;用户输入他的电子邮件地址

3>应用程序创建新的令牌,并在查询字符串中发送带有令牌的返回URL的电子邮件。

4>用户点击电子邮件中的链接并重定向到ResetPassword页面。

5个用户输入新密码并点击提交。

6个应用程序验证用户和令牌并重置密码。

问题。

1>收到电子邮件后,用户可能无法立即点击该链接,他可能会在一段时间后甚至几天后点击。在ASP.NET应用程序存储令牌的位置之间?我没有在数据库中看到它。

2 - ;令牌上是否有任何过期。

3>忘记密码屏幕用户输入他的电子邮件并单击提交以接收带有令牌的电子邮件。假设他这样做了3次。所以他收到3封带有3个不同令牌的电子邮件。然后他点击任意一封电子邮件中的链接并重置密码。其他2封电子邮件中未使用的令牌会发生什么情况,它们仍然有效吗?用户可以点击其他电子邮件中的链接并重置密码吗?

1 个答案:

答案 0 :(得分:3)

基本上,如果你看一下ASP.Net Identity创建的用户,你会看到一个基本为哈希的列SecurityStamp,用于所有与密码相关的场景。事实上,当用户更改密码时,它会被更改。

当您点击重置时,与该用户关联的密码SecurityStamp用于生成通过电子邮件发送的令牌。请参阅此处GenerateAsync方法中的源代码(有2个提供商)

https://github.com/aspnet/Identity/blob/dev/src/Microsoft.AspNetCore.Identity/TotpSecurityStampBasedTokenProvider.cs

public virtual async Task<string> GenerateAsync(string purpose, UserManager<TUser> manager, TUser user)
    {
        if (manager == null)
        {
            throw new ArgumentNullException(nameof(manager));
        }
        var token = await manager.CreateSecurityTokenAsync(user);
        var modifier = await GetUserModifierAsync(purpose, manager, user);
        return Rfc6238AuthenticationService.GenerateCode(token, modifier).ToString("D6", CultureInfo.InvariantCulture);
}

https://github.com/aspnet/Identity/blob/dev/src/Microsoft.AspNetCore.Identity/DataProtectionTokenProvider.cs

    public virtual async Task<string> GenerateAsync(string purpose, UserManager<TUser> manager, TUser user)
    {
        if (user == null)
        {
            throw new ArgumentNullException(nameof(user));
        }
        var ms = new MemoryStream();
        var userId = await manager.GetUserIdAsync(user);
        using (var writer = ms.CreateWriter())
        {
            writer.Write(DateTimeOffset.UtcNow);
            writer.Write(userId);
            writer.Write(purpose ?? "");
            string stamp = null;
            if (manager.SupportsUserSecurityStamp)
            {
                stamp = await manager.GetSecurityStampAsync(user);
            }
            writer.Write(stamp ?? "");
        }
        var protectedBytes = Protector.Protect(ms.ToArray());
        return Convert.ToBase64String(protectedBytes);
    }

一旦用户点击链接并提交在电子邮件中发送的新密码令牌,也会提交并针对SecurityStamp进行验证。

    public virtual async Task<bool> ValidateAsync(string purpose, string token, UserManager<TUser> manager, TUser user)
    {
        if (manager == null)
        {
            throw new ArgumentNullException(nameof(manager));
        }
        int code;
        if (!int.TryParse(token, out code))
        {
            return false;
        }
        var securityToken = await manager.CreateSecurityTokenAsync(user);
        var modifier = await GetUserModifierAsync(purpose, manager, user);
        return securityToken != null && Rfc6238AuthenticationService.ValidateCode(securityToken, code, modifier);
}

或者

    public virtual async Task<bool> ValidateAsync(string purpose, string token, UserManager<TUser> manager, TUser user)
    {
        try
        {
            var unprotectedData = Protector.Unprotect(Convert.FromBase64String(token));
            var ms = new MemoryStream(unprotectedData);
            using (var reader = ms.CreateReader())
            {
                var creationTime = reader.ReadDateTimeOffset();
                var expirationTime = creationTime + Options.TokenLifespan;
                if (expirationTime < DateTimeOffset.UtcNow)
                {
                    return false;
                }

                var userId = reader.ReadString();
                var actualUserId = await manager.GetUserIdAsync(user);
                if (userId != actualUserId)
                {
                    return false;
                }
                var purp = reader.ReadString();
                if (!string.Equals(purp, purpose))
                {
                    return false;
                }
                var stamp = reader.ReadString();
                if (reader.PeekChar() != -1)
                {
                    return false;
                }

                if (manager.SupportsUserSecurityStamp)
                {
                    return stamp == await manager.GetSecurityStampAsync(user);
                }
                return stamp == "";
            }
        }
        // ReSharper disable once EmptyGeneralCatchClause
        catch
        {
            // Do not leak exception
        }
        return false;
}

成功验证令牌身份系统后,会使用新的SecurityStamp进一步更新用户详细信息。随着所有回答你的问题

答案1 - 未存储令牌,电子邮件中的链接立即生效。我有一个经过全面测试的生产系统,我和用户都不必等待链接激活。

Ans 2-我认为默认值是1天。您可以通过在Create

ApplicationUserManager方法中添加以下代码来更改它
if (dataProtectionProvider != null)
 {
  manager.UserTokenProvider =
   new DataProtectorTokenProvider<ApplicationUser>
      (dataProtectionProvider.Create("ASP.NET Identity"))
      {                    
         TokenLifespan = TimeSpan.FromHours(1) //Any custom TimeSpan
      };
 }

答案3 - 只有1才能正常工作,即首先点击的用户,因为在此安全标记更改后,它将使其他2封电子邮件中的令牌无效。

希望这有帮助。