如何为IdentityServer、Web API和客户端应用设置Cookie过期时间及刷新Bearer令牌
问题根因
你遇到的现象核心是客户端Cookie有效期、Identity Server会话有效期、Access Token有效期三者不匹配:默认Identity Server4签发的Access Token有效期仅1小时,而你的客户端Cookie没有配置匹配的过期规则,就会出现「客户端看似保持登录、但调用API返回UNAUTHORIZED」的情况。
1. 控制客户端Cookie过期时长
修改客户端AddCookie配置即可自定义Cookie有效期、是否开启滑动过期:
.AddCookie(options => { options.Cookie.Name = "CorpLense.HR.WebClient"; // 自定义Cookie过期时长,示例设为8小时 options.ExpireTimeSpan = TimeSpan.FromHours(8); // 关闭滑动过期:到期直接失效;如果开启则用户在过期前半段时间内有访问,会自动刷新Cookie有效期 options.SlidingExpiration = false; })
ASP.NET Core认证中间件会自动检测Cookie有效性,Cookie过期后会自动触发登录跳转,无需额外处理。如果需要对齐服务端会话状态,可在配置中增加OnValidatePrincipal事件,每次请求时校验服务端会话是否有效,失效则自动清除Cookie触发登出。
2. Bearer令牌自动刷新
你已经开启了SaveTokens = true,客户端会将access_token、refresh_token、expires_at等值存储在Cookie中,可通过自定义请求拦截器实现Token静默刷新:
2.1 注册Token刷新拦截器
public class TokenRefreshHandler : DelegatingHandler { private readonly IHttpContextAccessor _httpContextAccessor; private readonly IConfiguration _configuration; public TokenRefreshHandler(IHttpContextAccessor httpContextAccessor, IConfiguration configuration) { _httpContextAccessor = httpContextAccessor; _configuration = configuration; } protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { var context = _httpContextAccessor.HttpContext; var accessToken = await context.GetTokenAsync("access_token"); var expiresAtStr = await context.GetTokenAsync("expires_at"); // 提前5分钟检查Token是否即将过期 if (DateTimeOffset.TryParse(expiresAtStr, out var expiresAt) && expiresAt < DateTimeOffset.UtcNow.AddMinutes(5)) { var refreshToken = await context.GetTokenAsync("refresh_token"); using var tokenClient = new HttpClient(); var tokenResp = await tokenClient.RequestRefreshTokenAsync(new RefreshTokenRequest { Address = $"{_configuration["IdentityServerUri"]}/connect/token", ClientId = "CorpLense.HR.WebClient", ClientSecret = "***", RefreshToken = refreshToken }); if (!tokenResp.IsError) { // 更新Cookie中存储的Token信息 var authResult = await context.AuthenticateAsync("Cookies"); authResult.Properties.UpdateTokenValue("access_token", tokenResp.AccessToken); authResult.Properties.UpdateTokenValue("refresh_token", tokenResp.RefreshToken); authResult.Properties.UpdateTokenValue("expires_at", DateTimeOffset.UtcNow.AddSeconds(tokenResp.ExpiresIn).ToString("o")); await context.SignInAsync("Cookies", authResult.Principal, authResult.Properties); accessToken = tokenResp.AccessToken; } else { // 刷新失败说明refresh_token也已过期,直接执行登出 await context.SignOutAsync("Cookies"); await context.SignOutAsync("oidc"); context.Response.Redirect("/"); return new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized); } } request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken); return await base.SendAsync(request, cancellationToken); } }
2.2 关联到API调用客户端
将拦截器注入到你调用Web API的HttpClient管道中,即可实现自动刷新:
services.AddHttpContextAccessor(); services.AddTransient<TokenRefreshHandler>(); services.AddHttpClient("ApiClient", client => { client.BaseAddress = new Uri(Configuration["ApiBaseUri"]); }).AddHttpMessageHandler<TokenRefreshHandler>();
3. 服务端全局控制Token与会话有效期
在Identity Server端注册客户端的配置中,可全局控制各类凭证的有效期:
new Client { ClientId = "CorpLense.HR.WebClient", // 其他原有配置 AccessTokenLifetime = 3600, // Access Token有效期,单位秒,默认3600(1小时) RefreshTokenLifetime = 86400, // Refresh Token滑动有效期,默认30天 RefreshTokenUsage = TokenUsage.OneTime, // 每次刷新后更换Refresh Token,安全性更高 AbsoluteRefreshTokenLifetime = 2592000, // Refresh Token绝对过期时间,默认90天 UserSsoLifetime = 28800, // SSO会话有效期,和客户端Cookie有效期保持一致即可 }
内容的提问来源于stack exchange,提问作者iffi
相关产品推荐
相关产品推荐

