.NET Core 3.1中OIDC刷新Access Token时OnValidatePrincipal未触发
我之前在做.NET Core + Keycloak的OIDC项目时,也碰到过一模一样的OnValidatePrincipal事件死活不触发的问题!咱们一步步来排查和解决:
首先,明确事件触发的核心逻辑
OnValidatePrincipal事件不是每次请求都会执行,它只会在Cookie需要被验证或刷新的特定时机触发:
- 当Cookie的
ExpireTimeSpan临近到期(结合SlidingExpiration配置) - 当用户访问带有
[Authorize]特性的页面时,系统会主动检查Cookie有效性
1. 检查Cookie与OIDC的关联配置
先确保你的Cookie认证配置和Keycloak的OIDC配置正确绑定,并且设置了合理的过期策略:
services.AddAuthentication(options => { // 默认用Cookie维护登录状态 options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; // 未认证时跳转到Keycloak登录 options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; }) .AddCookie(options => { // 关键:把Cookie过期时间和Keycloak的Access Token过期时间对齐(Keycloak默认1小时) options.ExpireTimeSpan = TimeSpan.FromHours(1); // 启用滑动刷新:用户活跃时自动延长Cookie有效期 options.SlidingExpiration = true; // 配置令牌刷新逻辑 options.Events = new CookieAuthenticationEvents { OnValidatePrincipal = async context => { // 先获取令牌过期时间 var expiresAtStr = context.Properties.GetTokenValue("expires_at"); if (DateTimeOffset.TryParse(expiresAtStr, out var expiration) && expiration <= DateTimeOffset.UtcNow.AddMinutes(5)) { // 拿到Refresh Token去Keycloak刷新令牌 var refreshToken = context.Properties.GetTokenValue("refresh_token"); // --- 这里实现调用Keycloak刷新接口的逻辑 --- // 比如用HttpClient请求Keycloak的token端点,获取新的access_token、refresh_token、expires_at // 更新Cookie中的令牌信息 context.Properties.UpdateTokenValue("access_token", newAccessToken); context.Properties.UpdateTokenValue("refresh_token", newRefreshToken); context.Properties.UpdateTokenValue("expires_at", newExpiration.ToString()); // 标记需要更新Cookie context.ShouldRenew = true; } } }; }) .AddOpenIdConnect(options => { // Keycloak基础配置 options.Authority = "https://your-keycloak-domain/auth/realms/your-realm"; options.ClientId = "your-client-id"; options.ClientSecret = "your-client-secret"; options.ResponseType = "code"; options.Scope.Add("openid"); options.Scope.Add("profile"); // 确保OIDC用默认Cookie方案保存登录状态 options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; // 必须开启:把Refresh Token等信息保存到Cookie中 options.SaveTokens = true; });
2. 确认中间件顺序绝对正确
在Program.cs(或旧版的Startup.cs)中,中间件的顺序必须严格遵循以下逻辑,否则认证检查不会触发:
var app = builder.Build(); // 静态文件等中间件放在前面 app.UseStaticFiles(); app.UseRouting(); // 必须先加认证,再加授权! app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); endpoints.MapRazorPages(); });
如果UseAuthentication()在UseAuthorization()之后,系统会跳过Cookie验证流程,OnValidatePrincipal自然不会执行。
3. 验证Token是否被正确保存
登录成功后,你可以在控制器中通过以下代码检查Refresh Token是否被保存到Cookie中:
var refreshToken = await HttpContext.GetTokenAsync("refresh_token");
如果获取不到,检查OIDC配置中是否开启了options.SaveTokens = true;——这是刷新令牌的前提。
4. 手动测试触发时机
如果配置都没问题,你可以模拟Access Token过期的场景来测试:
- 登录后,通过
HttpContext.GetTokenAsync("expires_at")拿到令牌过期时间 - 把本地系统时间调到过期时间之后的5分钟内
- 访问带有
[Authorize]的Privacy页面,此时应该会触发OnValidatePrincipal事件
最后,避开一个常见误区
不要把Cookie的ExpireTimeSpan设得过长(比如24小时),而Keycloak的Access Token只有1小时。这种情况下,Cookie还没到滑动刷新的触发条件,Access Token就已经过期了,此时访问授权页面会直接跳转到Keycloak重新登录,而不会触发令牌刷新逻辑。
内容的提问来源于stack exchange,提问作者Aarondk1
相关产品推荐
相关产品推荐

