【问题标题】:Validate authentication cookie with ASP.NET Core 2.1 / 3+ Identity使用 ASP.NET Core 2.1 / 3+ Identity 验证身份验证 cookie
【发布时间】:2018-12-30 07:09:48
【问题描述】:

在 ASP.NET Core 2 中使用 Cookie 身份验证(有或没有 Identity)时,可能会发生用户的电子邮件或姓名被更改,甚至在 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();
            }
        }
    };
});

【问题讨论】:

  • 身份使用安全标记,它是存储在数据库中针对用户的 GUID。每当帐户更改时都会更新它,并定期与存储在 cookie 中的现有安全标记进行比较(不同时会失效)。
  • @MarkG 我们如何影响与持久存储进行比较的频率。对于每个请求,每天一次,...?这是关于可能的性能损失mentioned in the docs
  • Identity 使用 ValidationInterval 属性,默认为 30 分钟。

标签: c# asp.net-core cookies asp.net-core-3.1 asp.net-core-identity


【解决方案1】:

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

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

与身份验证cookie验证无关

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

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。这是一种确保用户正在积极使用应用程序的方法,而不是例如让设备无人看管。否定的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);
});

验证间隔

默认为TimeSpan.FromMinutes(30)

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

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

Startup中加载服务的顺序

需要注意的是:services. AddIdentity() 还为身份验证 cookie 设置了默认值。如果您在services.ConfigureApplicationCookie() 之后添加它,这将覆盖之前的设置。 在上面的前面几个之后我打电话给services.Configure&lt;SecurityStampValidatorOptions&gt;()

再次感谢 @MarkG 为您指明方向。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-10-29
    • 2017-03-30
    • 2018-05-10
    • 1970-01-01
    • 1970-01-01
    • 2022-01-23
    • 2018-12-29
    • 2019-02-25
    相关资源
    最近更新 更多