自定义默认策略下多认证方案的角色授权无法适配所有方案
问题背景
配置了Default和Secondary两个JWT认证方案,通过OnTokenValidated事件为两种Token都添加了Administrator角色。自定义默认认证策略包含这两种方案,无角色要求的接口(如.RequireAuthorization())两种Token均可正常访问,但带角色要求的接口(如.RequireRole("Administrator"))只有DefaultAuthenticateScheme指定的方案Token能通过授权;切换默认方案后,支持的Token也随之反转。非最小API使用[Authorize]或[Authorize(Roles = "Administrator")]时问题完全一致。
问题根源
ASP.NET Core的默认角色授权逻辑(包括RequireRole扩展方法、[Authorize(Roles)]特性)会默认仅使用默认认证方案对应的用户身份来校验角色,哪怕你配置了包含多方案的认证策略,角色校验环节不会自动遍历所有已通过认证的身份信息。
解决方案
方案1:自定义角色授权策略(推荐,灵活可控)
创建一个自定义授权策略,明确要求校验所有已通过认证的身份的角色,而非仅默认方案的身份。
步骤1:配置自定义策略
在Program.cs的授权配置中添加自定义策略:
builder.Services.AddAuthorization(options => { // 保留原有的默认认证策略,支持两种方案的身份认证 options.DefaultPolicy = new AuthorizationPolicyBuilder() .AddAuthenticationSchemes("Default", "Secondary") .RequireAuthenticatedUser() .Build(); // 自定义支持多方案的角色策略 options.AddPolicy("MultiSchemeAdministrator", policy => { policy.AddAuthenticationSchemes("Default", "Secondary"); policy.RequireAssertion(context => { // 遍历所有已认证的身份,检查是否存在包含目标角色的身份 return context.User.Identities.Any(identity => identity.IsAuthenticated && identity.HasClaim(c => c.Type == ClaimTypes.Role && c.Value == "Administrator")); }); }); });
步骤2:接口绑定自定义策略
- 最小API场景:
app.MapGet("/two", () => "Access granted for /two") .RequireAuthorization("MultiSchemeAdministrator"); - MVC/API控制器场景:
[Authorize(Policy = "MultiSchemeAdministrator")] public IActionResult Two() { return Ok("Access granted for /two"); }
方案2:全局替换默认角色授权处理器
如果希望保留原有的RequireRole或[Authorize(Roles)]用法,无需每个接口指定自定义策略,可以替换默认的角色授权处理器,让它支持多认证方案:
步骤1:实现自定义授权处理器
public class MultiSchemeRoleAuthorizationHandler : AuthorizationHandler<RolesAuthorizationRequirement> { protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, RolesAuthorizationRequirement requirement) { // 检查所有已认证的身份中,是否有任意一个包含要求的角色 var hasValidRole = context.User.Identities .Where(identity => identity.IsAuthenticated) .Any(identity => requirement.AllowedRoles.Any(role => identity.HasClaim(ClaimTypes.Role, role))); if (hasValidRole) { context.Succeed(requirement); } return Task.CompletedTask; } }
步骤2:注册自定义处理器
在Program.cs中添加服务注册:
builder.Services.AddSingleton<IAuthorizationHandler, MultiSchemeRoleAuthorizationHandler>();
配置完成后,原有的.RequireRole("Administrator")或[Authorize(Roles = "Administrator")]就能自动支持两种认证方案的Token了。
关键验证点
确保两个JWT认证方案的OnTokenValidated事件都正确添加了角色声明,示例如下:
// 配置Default方案时的事件 options.Events = new JwtBearerEvents { OnTokenValidated = context => { if (context.Principal?.Identity is ClaimsIdentity claimsIdentity) { claimsIdentity.AddClaim(new Claim(ClaimTypes.Role, "Administrator")); } return Task.CompletedTask; } }; // 配置Secondary方案时同样要添加该事件逻辑
内容的提问来源于stack exchange,提问作者ProgrammingLlama

