.NET 6 Ocelot网关自定义认证处理器:兼容SSO JWT与专属ID认证
我有一个基于.NET 6.0.0(使用Ocelot)的网关API,需要支持两种认证方式:
- Azure AD生成的SSO JWT令牌认证
- 应用生态专属ID认证
但Ocelot每个端点仅允许指定一个AuthenticationProviderKey,所以我打算写一个自定义认证处理器,根据请求中的logintype头自动选择认证逻辑。现在卡在如何把原有的Azure AD SSO认证逻辑复用到自定义处理器里——原逻辑完全依赖Microsoft提供的Authority、MetadataAddress等配置,不知道怎么在自定义处理器里复用这套外部令牌验证逻辑。
原Azure AD认证配置代码
var authenticationProviderKey = "AzureAdJwtBearer"; builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(authenticationProviderKey, opt => { opt.Audience = config.GetValue<string>("AzureAd:Audience"); opt.Authority = $"address1" + config.GetValue<string>("AzureAd:TenantId"); opt.MetadataAddress = $"address2" + config.GetValue<string>("AzureAd:TenantId") + "/v2.0/.well-known/openid-configuration"; });
我尝试的未完成实现
Program.cs 修改后的代码
builder.Services.AddAuthentication(options => options.DefaultScheme = TwoAuthSchemeOptions.Name) .AddScheme<TwoAuthsSchemeOptions, TwoAuthsHandler>(TwoAuthsSchemeOptions.Name, options => { });
TwoAuthsSchemeOptions.cs
public class TwoAuthsSchemeOptions : AuthenticationSchemeOptions { public const string Name = "TwoAuthsScheme"; }
TwoAuthsHandler.cs(未完成)
public class TwoAuthsHandler : AuthenticationHandler<TwoAuthsSchemeOptions> { public TwoAuthsHandler(IOptionsMonitor<TwoAuthsSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { } protected override Task<AuthenticateResult> HandleAuthenticateAsync() { var loginTypeHeader = Request.Headers["logintype"].ToString(); switch (loginTypeHeader) { case "sso": try { // 这里需要复用原有的Azure AD SSO认证逻辑 return Task.FromResult(AuthenticateResult.Success(ticket)); } catch(Exception ex) { return Task.FromResult(AuthenticateResult.Fail("User authentication failed or session expired")); } case "special": try { // 专属ID认证逻辑已实现 return Task.FromResult(AuthenticateResult.Success(ticket)); } catch { return Task.FromResult(AuthenticateResult.Fail("User authentication failed or session expired")); } default: return Task.FromResult(AuthenticateResult.Fail("User authentication failed or session expired")); } } }
遇到的困境
我不能直接用硬编码的TokenValidationParameters来验证令牌(如下方代码),因为Azure AD的令牌是外部生成的,原认证逻辑完全依赖Authority和MetadataAddress来自动获取签名密钥、颁发者等信息,不知道怎么在自定义处理器里实现这套逻辑。
tokenHandler.ValidateToken(token, new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("secret")), ValidateIssuer = true, ValidIssuer = "someissuer", ValidAudience = "someaudience", ValidateAudience = true, ClockSkew = TimeSpan.Zero }, out SecurityToken securityToken);
核心思路是在自定义处理器中复用已注册的JwtBearer认证服务,而不是自己重新实现Azure AD的令牌验证逻辑。
步骤1:保留原Azure AD JwtBearer配置
不要移除原有的AddJwtBearer配置,同时注册自定义认证方案:
var authenticationProviderKey = "AzureAdJwtBearer"; // 保留原Azure AD JWT认证配置 builder.Services.AddAuthentication(options => { options.DefaultScheme = TwoAuthsSchemeOptions.Name; }) .AddJwtBearer(authenticationProviderKey, opt => { opt.Audience = config.GetValue<string>("AzureAd:Audience"); opt.Authority = $"address1" + config.GetValue<string>("AzureAd:TenantId"); opt.MetadataAddress = $"address2" + config.GetValue<string>("AzureAd:TenantId") + "/v2.0/.well-known/openid-configuration"; }) // 注册自定义认证方案 .AddScheme<TwoAuthsSchemeOptions, TwoAuthsHandler>(TwoAuthsSchemeOptions.Name, options => { });
步骤2:修改自定义处理器,注入JwtBearerHandler
在TwoAuthsHandler中注入IAuthenticationHandlerProvider和IAuthenticationSchemeProvider,用来获取已注册的Azure AD JwtBearer处理器,直接调用它的认证逻辑:
public class TwoAuthsHandler : AuthenticationHandler<TwoAuthsSchemeOptions> { private readonly IAuthenticationHandlerProvider _handlerProvider; private readonly IAuthenticationSchemeProvider _schemeProvider; public TwoAuthsHandler( IOptionsMonitor<TwoAuthsSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, IAuthenticationHandlerProvider handlerProvider, IAuthenticationSchemeProvider schemeProvider) : base(options, logger, encoder, clock) { _handlerProvider = handlerProvider; _schemeProvider = schemeProvider; } protected override async Task<AuthenticateResult> HandleAuthenticateAsync() { var loginTypeHeader = Request.Headers["logintype"].ToString(); switch (loginTypeHeader) { case "sso": // 获取Azure AD JwtBearer的认证方案 var azureAdScheme = await _schemeProvider.GetSchemeAsync("AzureAdJwtBearer"); if (azureAdScheme == null) { return AuthenticateResult.Fail("Azure AD authentication scheme not found"); } // 获取对应的认证处理器 var jwtHandler = await _handlerProvider.GetHandlerAsync(Context, azureAdScheme.Name) as JwtBearerHandler; if (jwtHandler == null) { return AuthenticateResult.Fail("Failed to get Azure AD authentication handler"); } // 直接调用JwtBearer的认证逻辑 var ssoResult = await jwtHandler.AuthenticateAsync(); return ssoResult.Succeeded ? ssoResult : AuthenticateResult.Fail("SSO authentication failed: " + ssoResult.Failure.Message); case "special": // 执行专属ID认证逻辑 var specialId = Request.Headers["special-id"].ToString(); if (string.IsNullOrEmpty(specialId) || !ValidateSpecialId(specialId)) { return AuthenticateResult.Fail("Invalid special ID"); } var claims = new List<Claim> { new Claim(ClaimTypes.NameIdentifier, specialId), new Claim("logintype", "special") }; var identity = new ClaimsIdentity(claims, TwoAuthsSchemeOptions.Name); var ticket = new AuthenticationTicket(new ClaimsPrincipal(identity), TwoAuthsSchemeOptions.Name); return AuthenticateResult.Success(ticket); default: return AuthenticateResult.Fail("Invalid login type"); } } // 替换成实际的专属ID验证逻辑 private bool ValidateSpecialId(string specialId) { return specialId.StartsWith("APP_") && specialId.Length == 16; } }
步骤3:Ocelot配置中指定自定义认证方案
在Ocelot的路由配置里,将AuthenticationProviderKey设置为自定义方案的名称TwoAuthsScheme:
"Routes": [ { "DownstreamPathTemplate": "/api/{everything}", "DownstreamScheme": "https", "DownstreamHostAndPorts": [ { "Host": "your-backend-service", "Port": 443 } ], "UpstreamPathTemplate": "/api/{everything}", "AuthenticationOptions": { "AuthenticationProviderKey": "TwoAuthsScheme", "AllowedScopes": [] } } ]
关键说明
- 复用官方JwtBearer逻辑:通过
IAuthenticationHandlerProvider获取已配置好的JwtBearerHandler,直接调用其AuthenticateAsync方法,完全复用Azure AD的令牌验证逻辑(包括自动从Metadata地址获取签名密钥、验证颁发者/受众等)。 - 避免重复配置:原有的
AddJwtBearer配置不需要修改,确保所有Azure AD的验证参数都保持一致。 - 灵活扩展:如果以后需要增加其他认证方式,只需要在
switch分支中添加对应的逻辑即可。
内容的提问来源于stack exchange,提问作者T4under4

