如何用.NET JWT中间件同时验证Azure AD令牌与自定义JWT?
我来帮你梳理几个解决这个问题的可行方案,你可以根据自己的实际场景来选:
方案一:配置多个JWT验证方案,允许任一验证通过
你可以通过注册两个独立的JWT Bearer验证方案(分别对应Azure AD和自定义JWT),然后创建一个授权策略,只要其中一个方案验证通过就允许访问。这种方式利用了.NET身份验证系统的原生能力,代码简洁易维护。
具体实现步骤:
- 在
Startup.cs中注册两个验证方案,并配置对应的验证参数:
var azureAdAuthOptions = new AuthenticationOptions(); configuration.Bind("AzureAd", azureAdAuthOptions); // 注册Azure AD的JWT验证方案 services.AddAuthentication() .AddJwtBearer("AzureADJwt", options => { options.Authority = $"{azureAdAuthOptions.Instance}{azureAdAuthOptions.TenantId}"; options.TokenValidationParameters = new TokenValidationParameters { ValidAudience = azureAdAuthOptions.ClientId, ValidIssuer = $"{azureAdAuthOptions.Instance}{azureAdAuthOptions.TenantId}/v2.0" }; }) // 注册自定义JWT的验证方案 .AddJwtBearer("CustomJwt", options => { var mySecret = configuration["CustomJwt:Secret"]; // 生产环境建议从配置读取,不要硬编码 var mySecurityKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(mySecret)); options.TokenValidationParameters = new TokenValidationParameters { ValidAudience = azureAdAuthOptions.ClientId, ValidIssuer = $"{azureAdAuthOptions.Instance}{azureAdAuthOptions.TenantId}/v2.0", IssuerSigningKey = mySecurityKey, ValidateIssuerSigningKey = true }; }); // 创建默认授权策略:允许任一验证方案通过 services.AddAuthorization(options => { options.DefaultPolicy = new AuthorizationPolicyBuilder() .RequireAuthenticatedUser() .AddAuthenticationSchemes("AzureADJwt", "CustomJwt") .Build(); });
- 之后你现有的
[Authorize]标签不需要任何修改,因为我们把这个"二选一"策略设为了全局默认;如果你只想给部分端点用这个规则,也可以创建命名策略,用[Authorize(Policy = "AllowEitherJwt")]指定。
方案二:生成能通过Azure AD验证的自定义令牌?(不可行)
直接生成能通过现有Azure AD验证逻辑的令牌是完全不可能的。Azure AD的令牌是用它自己的私有密钥签名的,这个密钥属于微软Azure的安全体系,你没有任何权限获取或使用。你的自定义令牌没有Azure AD的合法签名,必然会被现有验证逻辑拒绝,所以这个方向不用考虑。
方案三:自定义授权属性,实现"或"逻辑
如果你不想修改全局的验证配置,也可以自定义一个授权处理程序,手动实现"Azure AD验证通过 或 自定义JWT验证通过"的逻辑。
具体实现步骤:
- 自定义授权需求和处理程序:
// 定义授权需求 public class EitherAuthorizationRequirement : IAuthorizationRequirement { } // 实现授权逻辑 public class EitherAuthorizationHandler : AuthorizationHandler<EitherAuthorizationRequirement> { private readonly IAuthenticationService _authenticationService; public EitherAuthorizationHandler(IAuthenticationService authenticationService) { _authenticationService = authenticationService; } protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, EitherAuthorizationRequirement requirement) { var httpContext = context.Resource as HttpContext; if (httpContext == null) { context.Fail(); return; } // 先尝试Azure AD验证 var azureAdResult = await _authenticationService.AuthenticateAsync(httpContext, "AzureADJwt"); if (azureAdResult.Succeeded) { context.Succeed(requirement); return; } // 再尝试自定义JWT验证 var customJwtResult = await _authenticationService.AuthenticateAsync(httpContext, "CustomJwt"); if (customJwtResult.Succeeded) { context.Succeed(requirement); return; } // 两种验证都失败则拒绝访问 context.Fail(); } }
- 在
Startup.cs中注册处理程序和对应的策略:
services.AddScoped<IAuthorizationHandler, EitherAuthorizationHandler>(); services.AddAuthorization(options => { options.AddPolicy("AllowEitherAuth", policy => policy.Requirements.Add(new EitherAuthorizationRequirement())); });
- 使用时给需要的端点添加标注:
[Authorize(Policy = "AllowEitherAuth")]
方案建议
优先选择方案一,因为它完全基于.NET身份验证系统的原生能力,代码最简洁,后续维护成本最低。另外注意:
- 生产环境中不要硬编码自定义JWT的密钥,建议从配置文件或Azure Key Vault等密钥管理服务读取
- 自定义JWT的
Issuer和Audience要和验证配置完全一致,否则会验证失败
内容的提问来源于stack exchange,提问作者PalBo
相关产品推荐
相关产品推荐

