代码之家  ›  专栏  ›  技术社区  ›  Linda Lawton - DaImTo

cookie不会在服务器上滑动,但在本地运行时会滑动。

  •  1
  • Linda Lawton - DaImTo  · 技术社区  · 7 年前

    我有一个具有ASP.NET标识的Identity Server 4应用程序。我把饼干准备好滑动。

    services.ConfigureApplicationCookie(opts =>
                            {
                                opts.Cookie.Expiration = TimeSpan.FromDays(30);
                                opts.SessionStore = new RedisCacheTicketStore(new RedisCacheOptions()
                                {
                                    Configuration = configuration["Redis:HostPort"]
                                }, logger, configuration);
                                opts.Cookie.SameSite = SameSiteMode.None;
                                opts.SlidingExpiration = true;
                                opts.ExpireTimeSpan = TimeSpan.FromDays(30);
                            }
                        );
    

    不滑动

    localhost:当用户登录时 .AspNetCore.Idenitty.Application 获取过期时间。当刷新页面时,过期时间被更新,我可以看到时间戳的更改。

    生产:但是,如果我在服务器上检查它,那么用户登录和。 AspNetCore.Idenitty.Application 获取具有登录时间戳的过期时间。但是,刷新页面时,时间戳不会更改。它与用户登录时保持不变。

    enter image description here

    用户在30分钟后退出

    生产:第二个问题是,正如您所看到的,过期时间是提前一个月设置的,但是在服务器上30分钟后,该用户将被踢出并强制再次登录。我不能让用户登录超过30分钟,即使他们是活跃的。

    安全印章

    我已经检查了用户安全戳没有更改,令牌包含 "AspNet.Identity.SecurityStamp": "[users actual key]"

    更新

    因此,经过一些挖掘,我最终决定重新进行安全戳验证。我在我的ApplicationSignInManager中通过使用以下方法来实现这一点

     public override async Task<ApplicationUser> ValidateSecurityStampAsync(ClaimsPrincipal principal)
            {
                if (principal == null)
                {
                    Logger.LogError(LoggingEvents.ApplicationSignInManagerSecurityTokenValidation, "ClaimsPrincipal is null");
                    return null;
                }
                var user = await UserManager.GetUserAsync(principal);
                if (await ValidateSecurityStampAsync(user, principal.FindFirstValue(Options.ClaimsIdentity.SecurityStampClaimType)))
                {
                    return user;
                }
    
                if(user == null)
                    Logger.LogError(LoggingEvents.ApplicationSignInManagerSecurityTokenValidation, "User not found [principal {principal}]", principal);
    
                var principalSecurityStamp = principal.FindFirstValue(Options.ClaimsIdentity.SecurityStampClaimType);  // Security stamp from claims
                var userManagerSecurityStamp = user.SecurityStamp;                                                     // Security Stamp from usermanager
                var getSecurityStampAsyncResults = await UserManager.GetSecurityStampAsync(user);                      // Security stamp from GetSecurityStampAsync
                Logger.LogError(LoggingEvents.ApplicationSignInManagerSecurityTokenValidation,
                    "Security stamp Validation Failed: [principalSecurityStamp {principalSecurityStamp}] != [getSecurityStampAsyncResults {getSecurityStampAsyncResults}] also ([userManagerSecurityStamp {userManagerSecurityStamp}] )", principalSecurityStamp, getSecurityStampAsyncResults, userManagerSecurityStamp);
    
                return null;
            }
    
            public virtual async Task<bool> ValidateSecurityStampAsync(ApplicationUser user, string securityStamp)
                => user != null &&
                   // Only validate the security stamp if the store supports it
                   (!UserManager.SupportsUserSecurityStamp || securityStamp == await UserManager.GetSecurityStampAsync(user));
    

    这导致一些非常有趣的信息立即出现在我的日志中。

    安全戳验证失败:【PrincipalSecurityStamp(空)】!=[GetSecuritySampasyncResults 83270B3F-A042-4A8F-B090-F5E1A084074E]还([UserManagerSecurityStamp 83270B3F-A042-4A8F-B090-F5E1A084074E])

    所以 principal.FindFirstValue(Options.ClaimsIdentity.SecurityStampClaimType) 似乎为空。为什么我不知道。我也不知道如何修复它,因为有许多第三方应用程序调用这个身份服务器。

    更新2:

    我现在可以验证generateclaimsasync是否设置了securitySampleClaim。然而,validateAsync中的cookievalidatePrincipalContext不包含有问题的声明,正如对该方法的评论所说,这很奇怪。

    /// <param name="context">The context containing the <see cref="System.Security.Claims.ClaimsPrincipal"/>
    
    2 回复  |  直到 7 年前
        1
  •  0
  •   Tratcher    7 年前

    SlidingExpiration 设置为true,指示处理程序在处理超过到期窗口一半的请求时,用新的到期时间重新发布新cookie。“对于30天的到期窗口,在前15天内不会发布新cookie。15天后的第一个请求将发出一个刷新的cookie。

    30分钟的超时可能来自 security stamp validator ,仅每30分钟运行一次(验证成本很高)。听起来您的邮票生成或验证不正确。您是否配置或自定义了该组件?

    侧注:拆卸 opts.Cookie.Expiration 它被忽略了。

        2
  •  0
  •   Linda Lawton - DaImTo    7 年前

    要找到这个问题的根源需要相当长的时间。如果有人遇到这个问题,我会在这里解释。

    首先,问题在于安全令牌。创建identity.application cookie时,安全令牌存储在用户表中。此令牌存储在cookie中。应用程序每五分钟联系一次Identity Server,检查令牌是否需要验证。如果它超过30分钟,那么安全令牌将被验证。(注意,5分钟和30分钟时间都是可配置的,这只是默认值)

    这是用来做一个叫做“从哪里注销”的东西。如果更改密码,用户表中您所在行的安全令牌将被更新。通过使其与所有设备上存储在cookie中的不同来实现。这将迫使您从哪里注销。

    发行NR一

    SignInManager.cs#L260 验证安全令牌,但不测试它是否为空。

    因此,如果cookie有问题,并且由于某种原因令牌在数据库中为空,或者在我的情况下,它已被另一个cookie重写,那么用户将登录30分钟,然后在第一次尝试验证安全令牌时被踢出。导致发出请求 #7055 . 每次都应该测试cookie,以确保其中包含安全令牌。

    发行NR 2

    以下代码行在用户中签名并创建cookie,将安全令牌存储在所述cookie中

    var signInUserResult = await _signInManager.PasswordSignInAsync(userName, password, rememberMe, true);
    

    经过大量的挖掘和调试,我发现下面这一行用一个不包含安全令牌的新cookie重写了原始cookie。

    await HttpContext.SignInAsync(user.Id.ToString(), user.UserName, props);