ASP.NET Core中AuthorizationHandlerContext.User填充位置及多IDP授权异常
咱们先解决你第一个核心问题:AuthorizationHandlerContext.User 是从哪里来的?
它的填充流程其实很清晰:
- 首先是认证中间件(在你的场景里就是 JWT Bearer 认证中间件)先处理 HTTP 请求,验证请求里的 JWT 令牌。如果验证通过,会生成一个包含令牌声明的
ClaimsPrincipal对象,并把它赋值给HttpContext.User。 - 当 ASP.NET Core 的授权中间件(
AuthorizationMiddleware)处理基于策略的授权时,会创建AuthorizationHandlerContext实例,它的User属性就是直接从当前请求的HttpContext.User复制过来的。
接下来聊聊你遇到的第二个 IDP 令牌的问题:日志明明显示认证成功,但 AuthorizationHandlerContext.User 是空对象。结合你的配置,我给你梳理几个大概率的排查方向:
1. 先确认认证后的 ClaimsPrincipal 真的有内容
日志里的「AuthenticationScheme: "Bearer" was successfully authenticated」只能说明令牌通过了签名、签发者等基础验证,但不代表生成的 ClaimsPrincipal 包含了预期的声明。你可以在认证中间件之后、授权中间件之前加一个调试中间件,直接打印 HttpContext.User 的所有声明:
app.Use(async (context, next) => { if (context.User.Identity?.IsAuthenticated == true) { var claimDetails = string.Join("\n", context.User.Claims.Select(c => $"{c.Type}: {c.Value}")); Console.WriteLine($"Authenticated user claims:\n{claimDetails}"); } await next(); });
如果这里打印出来的声明是空的,那问题出在 JWT Bearer 中间件的令牌解析/声明映射环节;如果有内容,那可能是后续中间件或者授权逻辑里修改了 User。
2. 检查 TokenValidationParameters 的声明映射配置
不同 IDP 签发的 JWT 声明字段可能不一样,比如有些 IDP 用 sub 作为用户唯一标识,而 ASP.NET Core 默认的 NameClaimType 是 http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier。如果第二个 IDP 的声明没有和框架的默认类型匹配,可能会导致 ClaimsPrincipal 看起来像“空对象”(其实是没有映射到预期的声明类型)。
你可以显式配置声明映射,比如:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidIssuers = new[] { "第一个IDP的地址", "第二个IDP的地址" }, IssuerSigningKeys = new[] { /* 两个IDP的公钥 */ }, // 根据第二个IDP的实际声明调整 NameClaimType = "sub", RoleClaimType = "role" }; });
3. 确认授权策略是否绑定了正确的认证方案
如果你的系统注册了多个认证方案(比如同时支持 Cookie 和 JWT),或者针对不同 IDP 配置了多个 JWT Bearer 方案,那需要确保你的授权策略明确指定了要使用的认证方案:
services.AddAuthorization(options => { options.AddPolicy("你的策略名称", policy => { // 指定使用 JWT Bearer 认证方案 policy.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme); policy.RequireAuthenticatedUser(); // 其他授权需求 }); });
4. 排查自定义 ClaimsTransformation 逻辑
如果你实现了 IClaimsTransformation 接口来转换用户声明,那要检查这个转换逻辑是否处理了第二个 IDP 的情况——比如是否因为识别不了第二个 IDP 的声明,把 ClaimsPrincipal 改成了空对象。
内容的提问来源于stack exchange,提问作者Kyle Krull

