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

时间:2019-01-21 11:56:27

标签: c# asp.net-core asp.net-identity identityserver4

我有一个具有asp .net身份的身份服务器4应用程序。我设置了可滑动的Cookie。

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(空)]!= [getSecurityStampAsyncResults 83270b3f-a042-4a8f-b090-f5e1a084074e]也([userManagerSecurityStamp 83270b3f-a042-4a8f-b090-f5e1a084074e])

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

update2:

我现在可以验证GenerateClaimsAsync确实设置了SecurityStampClaim。但是ValidateAsync中的CookieValidatePrincipalContext不包含有问题的声明,这很奇怪,因为对该方法的注释说。

/// <param name="context">The context containing the <see cref="System.Security.Claims.ClaimsPrincipal"/>

2 个答案:

答案 0 :(得分:0)

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

30分钟超时可能来自security stamp validator,{30}仅每30分钟运行一次(验证费用很高)。听起来您的邮票是错误生成或验证的。您是否完全配置或自定义了该组件?

旁注:删除opts.Cookie.Expiration,它将被忽略。

答案 1 :(得分:0)

要花很长时间才能弄清这个问题的根源。如果其他人遇到此问题,我将尝试在这里进行解释。

首先,问题是安全令牌。安全令牌存储在用户表中。创建identity.application cookie时,此令牌存储在cookie中。应用程序每五分钟与身份服务器联系一次,并检查是否需要验证令牌。如果它的时间超过30分钟,则将验证安全令牌。 (请注意,五分钟和三十分钟的时间都是可配置的,这只是默认设置)

这用于在任何地方进行注销。如果您更改密码,则用户表中行上的安全令牌将被更新。通过使其不同于所有设备中Cookie中存储的内容来实现。这将迫使您在任何地方注销。

第一个问题

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

因此,如果cookie出了点问题,并且令牌由于某种原因在数据库中或者由于我的原因令牌已为null,而该令牌已被另一个cookie覆盖,则用户将登录30分钟然后被踢出第一次尝试验证安全令牌。导致发布请求#7055。每次都要对Cookie进行测试,以确保其中包含安全令牌。

第2个问题

下面的代码行登录用户,并创建将安全令牌存储在该cookie中的cookie

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

经过大量的挖掘和调试,我发现以下行用新的不包含安全令牌的cookie覆盖了原始cookie。

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