使用ASP.NET Core 2.1身份验证身份验证cookie

axu*_*uno 7 c# cookies asp.net-core asp.net-core-identity

在ASP.NET Core 2(带有或不带有Identity)中使用Cookie身份验证时,可能会发生以下情况:更改用户的电子邮件或名称,甚至在cookie的生存期内删除帐户。这就是为什么文档指出cookie应该被验证的原因。文档中的示例带有注释

此处描述的方法是在每个请求上触发的。这可能会导致应用程序性能下降。

所以我想知道验证cookie主体的最佳模式什么。我所做的Startup.cs是订阅OnValidatePrincipal事件并通过例如LastValidatedOn对cookie 附加声明来每隔5分钟检查一次主记录的有效性:

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();
            }
        }
    };
});
Run Code Online (Sandbox Code Playgroud)

axu*_*uno 12

@MarkG向我指出了正确的方向,谢谢。具有仔细一看之后的源代码SecurityStampValidatorIdentity事我就明白了。实际上,我发布的问题的示例代码是不必要的,因为ASP.NET Core Identity可以以更好的方式立即使用该功能。

因为我还没有找到这样的总结,所以也许对其他人也有帮助。

与身份验证Cookie验证无关

...但还是很高兴知道...

services.ConfigureApplicationCookie(options =>
{
    options.Cookie.Expiration = TimeSpan.FromDays(30);
    options.ExpireTimeSpan = TimeSpan.FromDays(30);
    options.SlidingExpiration = true;
});
Run Code Online (Sandbox Code Playgroud)

ExpireTimeSpan

默认为 TimeSpan.FromDays(14)

身份验证票证的发行时间是cookie(CookieValidatePrincipalContext.Properties.IssuedUtc)的一部分。当cookie被发送回服务器,当前时间减去发行时间必须大于ExpireTimeSpan。如果不是,则用户将注销,而无需进一步调查。实际上,将设置为ExpireTimeSpan,将大多数与SlidingExpiration设置为一起使用true。这是确保用户正在积极使用该应用程序的手段,并且没有例如使设备无人看管的一种方式。负数TimeSpans将立即注销用户(但不会TimeSpan.Zero)。

控制身份验证Cookie验证需要什么

services.AddOptions();
services.Configure<SecurityStampValidatorOptions>(options =>
{
    // This is the key to control how often validation takes place
    options.ValidationInterval = TimeSpan.FromMinutes(5);
});
Run Code Online (Sandbox Code Playgroud)

验证间隔

默认为 TimeSpan.FromMinutes(30)

这确定了时间跨度,之后将针对持久性存储检查身份验证cookie的有效性。它是通过调用来实现SecurityStampValidator每个请求到服务器。如果当前时间减去Cookie的发布时间小于或等于 ValidationIntervalValidateSecurityStampAsync则将调用。这意味着 ValidationInterval = TimeSpan.Zero导致ValidateSecurityStampAsync针对每个请求调用。

注意 UserManager必须支持获取安全标记,否则它将失败。对于自定义用户管理器或用户存储,两者都必须正确实现IUserSecurityStampStore<TUser>

中的加载服务顺序 Startup

要注意的是:services. AddIdentity()还设置身份验证cookie的默认值。如果在services.ConfigureApplicationCookie()此之后添加,它将覆盖以前的设置。我打电话services.Configure<SecurityStampValidatorOptions>()给上面的那些人。

再次感谢@MarkG展示了方法。