AspnetIdentity 在哪里存储 UserTokens?
Where does AspnetIdentity stores UserTokens?
我有 ASP.Net MVC5 应用程序。在创建应用程序时 select 个人身份验证 visual studio 使用脚手架并添加用于登录、创建用户、忘记密码等的代码。我正在使用 ForgotPassword 功能的默认实现。这就是忘记密码的工作原理。
1> 用户点击忘记密码 link。
2> 用户输入他的电子邮件地址。
3> 应用程序创建新令牌并发送带有 return URL 且查询字符串中包含令牌的电子邮件。
4> 用户点击电子邮件中的 link 并被重定向到 ResetPassword 页面。
5> 用户输入新密码并点击提交。
6> 应用程序验证用户和令牌并重置密码。
问题。
1> 收到电子邮件后,用户可能不会立即点击 link,他可能会在一段时间甚至几天后点击。 ASP.NET 应用程序在哪里存储令牌?我没有在数据库中看到它。
2> 令牌是否过期。
3> 在忘记密码屏幕上,用户输入他的电子邮件并单击提交以接收带有令牌的电子邮件。假设他这样做了 3 次。所以他收到了 3 封电子邮件,其中包含 3 个不同的令牌。然后他从任何一封电子邮件中单击 link 并重置密码。其他 2 封电子邮件中未使用的令牌会怎样,它们是否仍然有效?用户可以在其他电子邮件中单击 link 并重设密码吗?
基本上,如果您查看由 ASP.Net Identity 创建的用户,您会看到一列 SecurityStamp
,它基本上是哈希值,用于所有与密码相关的场景。事实上,当用户更改其密码时,它也会更改。
当您点击重置密码时,与该用户关联的 SecurityStamp
用于生成通过电子邮件发送的令牌。在此处查看 GenerateAsync
方法中的源代码(有 2 个提供程序)
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);
}
或
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);
}
一旦用户点击 link 并提交在电子邮件中发送的新密码令牌也会被提交并根据 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
进一步更新用户详细信息。以上就是对您问题的回答
Ans 1 - 令牌未存储,电子邮件中的 link 立即激活。我有一个经过全面测试的生产系统,我和用户都不必等待 link 激活。
答案 2- 我认为默认值为 1 天。您可以通过在 ApplicationUserManager
Class
的 Create
方法中添加以下代码来更改它
if (dataProtectionProvider != null)
{
manager.UserTokenProvider =
new DataProtectorTokenProvider<ApplicationUser>
(dataProtectionProvider.Create("ASP.NET Identity"))
{
TokenLifespan = TimeSpan.FromHours(1) //Any custom TimeSpan
};
}
答案 3 - 只有 1 个会起作用,即无论哪个用户先点击,因为在安全标记更改之后,它将使其他 2 封电子邮件中的令牌失效。
希望这对您有所帮助。
我有 ASP.Net MVC5 应用程序。在创建应用程序时 select 个人身份验证 visual studio 使用脚手架并添加用于登录、创建用户、忘记密码等的代码。我正在使用 ForgotPassword 功能的默认实现。这就是忘记密码的工作原理。
1> 用户点击忘记密码 link。
2> 用户输入他的电子邮件地址。
3> 应用程序创建新令牌并发送带有 return URL 且查询字符串中包含令牌的电子邮件。
4> 用户点击电子邮件中的 link 并被重定向到 ResetPassword 页面。
5> 用户输入新密码并点击提交。
6> 应用程序验证用户和令牌并重置密码。
问题。
1> 收到电子邮件后,用户可能不会立即点击 link,他可能会在一段时间甚至几天后点击。 ASP.NET 应用程序在哪里存储令牌?我没有在数据库中看到它。
2> 令牌是否过期。
3> 在忘记密码屏幕上,用户输入他的电子邮件并单击提交以接收带有令牌的电子邮件。假设他这样做了 3 次。所以他收到了 3 封电子邮件,其中包含 3 个不同的令牌。然后他从任何一封电子邮件中单击 link 并重置密码。其他 2 封电子邮件中未使用的令牌会怎样,它们是否仍然有效?用户可以在其他电子邮件中单击 link 并重设密码吗?
基本上,如果您查看由 ASP.Net Identity 创建的用户,您会看到一列 SecurityStamp
,它基本上是哈希值,用于所有与密码相关的场景。事实上,当用户更改其密码时,它也会更改。
当您点击重置密码时,与该用户关联的 SecurityStamp
用于生成通过电子邮件发送的令牌。在此处查看 GenerateAsync
方法中的源代码(有 2 个提供程序)
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);
}
或
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);
}
一旦用户点击 link 并提交在电子邮件中发送的新密码令牌也会被提交并根据 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
进一步更新用户详细信息。以上就是对您问题的回答
Ans 1 - 令牌未存储,电子邮件中的 link 立即激活。我有一个经过全面测试的生产系统,我和用户都不必等待 link 激活。
答案 2- 我认为默认值为 1 天。您可以通过在 ApplicationUserManager
Class
Create
方法中添加以下代码来更改它
if (dataProtectionProvider != null)
{
manager.UserTokenProvider =
new DataProtectorTokenProvider<ApplicationUser>
(dataProtectionProvider.Create("ASP.NET Identity"))
{
TokenLifespan = TimeSpan.FromHours(1) //Any custom TimeSpan
};
}
答案 3 - 只有 1 个会起作用,即无论哪个用户先点击,因为在安全标记更改之后,它将使其他 2 封电子邮件中的令牌失效。
希望这对您有所帮助。