AspnetIdentity在哪里存储UserTokens?

AspnetIdentity在哪里存储UserTokens?

问题描述:

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

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

2>用户输入他的电子邮件地址。

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

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

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

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

问题。

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

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

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

+0

你能检查我的答案吗?我错过了什么 ? –

基本上,如果你看看由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天。您可以通过添加以下代码ApplicationUserManager

if (dataProtectionProvider != null) 
{ 
    manager.UserTokenProvider = 
    new DataProtectorTokenProvider<ApplicationUser> 
     (dataProtectionProvider.Create("ASP.NET Identity")) 
     {      
     TokenLifespan = TimeSpan.FromHours(1) //Any custom TimeSpan 
     }; 
} 

答3 Create方法改变它 - 仅1将工作即无论用户点击第一个因为安全戳更改后,它就会失效2个其他电子邮件令牌。

希望这会有所帮助。

+0

A谢谢你的详细解答 – LP13