在ASP.NET Core 2(带有或不带有Identity
)中使用Cookie身份验证时,可能会发生,更改用户的电子邮件或名称,甚至在cookie的生存期内删除帐户。这就是docs指出cookie应该被验证的原因。文档中的示例用
此处描述的方法是在每个请求上触发的。这个可以 导致该应用的性能下降。
所以我想知道什么是验证Cookie主体的最佳模式。我在Startup.cs
中所做的工作是订阅OnValidatePrincipal
事件并检查主数据的有效性,例如每5分钟在Cookie上附加一个LastValidatedOn
声明,如下所示:
services.ConfigureApplicationCookie(options =>
{
// other cookie options go here
options.Events.OnValidatePrincipal = async context =>
{
const string claimType = "LastValidatedOn";
const int reValidateAfterMinutes = 5;
if (!(context.Principal?.Identity is ClaimsIdentity claimIdentity)) return;
if (!context.Principal.HasClaim(c => c.Type == claimType) ||
DateTimeOffset.Now.UtcDateTime.Subtract(new DateTime(long.Parse(context.Principal.Claims.First(c => c.Type == claimType).Value))) > TimeSpan.FromMinutes(reValidateAfterMinutes))
{
var mgr = context.HttpContext.RequestServices.GetRequiredService<SignInManager<ApplicationUser>>();
var user = await mgr.UserManager.FindByNameAsync(claimIdentity.Name);
if (user != null && claimIdentity.Claims.FirstOrDefault(c => c.Type == "AspNet.Identity.SecurityStamp")?.Value == await mgr.UserManager.GetSecurityStampAsync(user))
{
claimIdentity.FindAll(claimType).ToList().ForEach(c => claimIdentity.TryRemoveClaim(c));
claimIdentity.AddClaim(new Claim(claimType, DateTimeOffset.Now.UtcDateTime.Ticks.ToString(), typeof(long).ToString()));
context.ShouldRenew = true;
}
else
{
context.RejectPrincipal();
await mgr.SignOutAsync();
}
}
};
});
答案 0 :(得分:4)
@MarkG向我指出了正确的方向,谢谢。在仔细查看SecurityStampValidator
和Identity
的{{3}}之后,对我来说很清楚。实际上,我发布的问题的示例代码是不必要的,因为ASP.NET Core Identity可以以更好的方式立即使用该功能。
由于我还没有找到这样的摘要,因此也许对其他人也有帮助。
...但是还是很了解...
services.ConfigureApplicationCookie(options =>
{
options.Cookie.Expiration = TimeSpan.FromDays(30);
options.ExpireTimeSpan = TimeSpan.FromDays(30);
options.SlidingExpiration = true;
});
ExpireTimeSpan
默认为TimeSpan.FromDays(14)
身份验证票证的发行时间是cookie(CookieValidatePrincipalContext.Properties.IssuedUtc
)的一部分。将Cookie发送回服务器后,当前时间减去发布时间必须比ExpireTimeSpan
更大。如果不是,则用户将注销,而无需进一步调查。实际上,设置ExpireTimeSpan
通常与将SlidingExpiration
设置为true
一起使用。这是确保用户正在积极使用该应用程序的手段,而不是例如使设备无人看管。否定的TimeSpan
将立即注销用户(但不会TimeSpan.Zero
)。
services.AddOptions();
services.Configure<SecurityStampValidatorOptions>(options =>
{
// This is the key to control how often validation takes place
options.ValidationInterval = TimeSpan.FromMinutes(5);
});
默认为TimeSpan.FromMinutes(30)
这确定了将针对永久存储检查身份验证cookie有效性的时间跨度。通过针对服务器的每个请求调用SecurityStampValidator
来完成此操作。如果当前时间减去Cookie的发布时间小于或等于 ValidationInterval
,则会调用ValidateSecurityStampAsync
。这表示
ValidationInterval = TimeSpan.Zero
导致为每个请求调用ValidateSecurityStampAsync
。
注意 UserManager
必须支持获取安全标记,否则将失败。对于自定义用户管理器或用户存储,两者都必须正确实现IUserSecurityStampStore<TUser>
。
Startup
中加载服务的顺序要注意的是:services. AddIdentity()
还设置了身份验证cookie的默认值。如果您在services.ConfigureApplicationCookie()
之后添加它,它将覆盖之前的设置。
我在上面的上一个之后叫services.Configure<SecurityStampValidatorOptions>()
。
再次感谢@MarkG展示了方法。