You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Core 3.1中OIDC刷新Access Token时OnValidatePrincipal未触发

解决CookieAuthenticationEvents.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过期的场景来测试:

  1. 登录后,通过HttpContext.GetTokenAsync("expires_at")拿到令牌过期时间
  2. 把本地系统时间调到过期时间之后的5分钟内
  3. 访问带有[Authorize]的Privacy页面,此时应该会触发OnValidatePrincipal事件

最后,避开一个常见误区

不要把Cookie的ExpireTimeSpan设得过长(比如24小时),而Keycloak的Access Token只有1小时。这种情况下,Cookie还没到滑动刷新的触发条件,Access Token就已经过期了,此时访问授权页面会直接跳转到Keycloak重新登录,而不会触发令牌刷新逻辑。

内容的提问来源于stack exchange,提问作者Aarondk1

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 16:17:54