12

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

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

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

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();
            }
        }
    };
});
4

1 回答 1

21

@MarkG 为我指明了正确的方向,谢谢。在仔细查看源代码SecurityStampValidator之后,Identity我就明白了。实际上,我在我的问题中发布的示例代码是不必要的,因为 ASP.NET Core Identity 以更好的方式提供了开箱即用的功能。

由于我还没有找到这样的摘要,也许它对其他人也有帮助。

与身份验证 cookie 验证无关的内容

...但仍然很高兴知道...

services.ConfigureApplicationCookie(options =>
{
    options.Cookie.Expiration = TimeSpan.FromDays(30);
    options.ExpireTimeSpan = TimeSpan.FromDays(30);
    options.SlidingExpiration = true;
});

过期时间跨度

默认为TimeSpan.FromDays(14)

身份验证票的签发时间是 cookie ( CookieValidatePrincipalContext.Properties.IssuedUtc) 的一部分。当 cookie 被发送回服务器时,当前时间减去发出时间必须大于. ExpireTimeSpan如果不是,用户将被注销而无需进一步调查。在实践中,设置, 大多与set toExpireTimeSpan一起使用。这是一种确保用户正在积极使用应用程序的方法,并且不会让设备无人看管。否定的 s 将立即注销用户(但不是)。SlidingExpirationtrueTimeSpanTimeSpan.Zero

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

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 为您指明方向。

于 2018-07-23T20:17:19.580 回答