.NET Core 6 Web API:多Bearer验证器,避免错误日志冗余
解决.NET Core 6 Web API多Bearer认证冗余日志问题
方法一:给每个认证方案添加颁发者预校验逻辑
在配置每个JWT Bearer认证方案时,通过Events在验证开始前先解析令牌的颁发者,若与当前方案的有效颁发者不匹配,直接终止当前方案的验证流程,避免产生错误日志。
示例代码:
services.AddAuthentication() .AddJwtBearer("Scheme1", options => { options.TokenValidationParameters = new TokenValidationParameters { ValidIssuer = "https://issuer1.com", // 其他验证参数(如ValidAudience、IssuerSigningKey等) }; options.Events = new JwtBearerEvents { OnMessageReceived = context => { var token = context.Request.Headers["Authorization"].ToString().Replace("Bearer ", ""); if (!string.IsNullOrEmpty(token)) { try { var handler = new JwtSecurityTokenHandler(); var jwtToken = handler.ReadJwtToken(token); if (jwtToken.Issuer != options.TokenValidationParameters.ValidIssuer) { // 颁发者不匹配,终止当前方案验证 context.Fail("Issuer does not match for this scheme"); } } catch { // 令牌格式无效,直接终止 context.Fail("Invalid token format"); } } return Task.CompletedTask; } }; }) .AddJwtBearer("Scheme2", options => { options.TokenValidationParameters = new TokenValidationParameters { ValidIssuer = "https://issuer2.com", // 其他验证参数 }; options.Events = new JwtBearerEvents { OnMessageReceived = context => { var token = context.Request.Headers["Authorization"].ToString().Replace("Bearer ", ""); if (!string.IsNullOrEmpty(token)) { try { var handler = new JwtSecurityTokenHandler(); var jwtToken = handler.ReadJwtToken(token); if (jwtToken.Issuer != options.TokenValidationParameters.ValidIssuer) { context.Fail("Issuer does not match for this scheme"); } } catch { context.Fail("Invalid token format"); } } return Task.CompletedTask; } }; });
这里的context.Fail()会标记当前认证方案失败,但不会抛出异常,也就不会生成冗余错误日志,后续认证方案仍会继续执行直到找到匹配的。
方法二:为不同端点指定专属认证方案
如果API端点可以按业务划分对应不同颁发者,直接在端点路由上指定允许的认证方案,让每个端点的请求只被对应验证器处理,从根源避免冗余校验。
示例代码:
// 全局为某组控制器指定认证方案 app.MapControllers() .RequireAuthorization(new AuthorizeAttribute { AuthenticationSchemes = "Scheme1" }); // 针对单个控制器/方法指定 [ApiController] [Route("api/[controller]")] [Authorize(AuthenticationSchemes = "Scheme2")] public class SecondController : ControllerBase { // 控制器方法逻辑 }
方法三:自定义JWT认证处理程序
继承JwtBearerHandler,重写HandleAuthenticateAsync方法,在验证前先检查令牌颁发者,不匹配则直接返回AuthenticateResult.NoResult(),跳过验证流程。
示例代码:
public class IssuerFilteredJwtHandler : JwtBearerHandler { private readonly string _validIssuer; public IssuerFilteredJwtHandler(IOptionsMonitor<JwtBearerOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, string validIssuer) : base(options, logger, encoder, clock) { _validIssuer = validIssuer; } protected override async Task<AuthenticateResult> HandleAuthenticateAsync() { var authHeader = Context.Request.Headers["Authorization"].ToString(); if (!authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) { return AuthenticateResult.NoResult(); } var token = authHeader.Substring("Bearer ".Length).Trim(); if (string.IsNullOrEmpty(token)) { return AuthenticateResult.NoResult(); } try { var handler = new JwtSecurityTokenHandler(); var jwtToken = handler.ReadJwtToken(token); if (jwtToken.Issuer != _validIssuer) { return AuthenticateResult.NoResult(); } } catch { return AuthenticateResult.NoResult(); } // 调用父类的标准验证逻辑 return await base.HandleAuthenticateAsync(); } }
注册自定义处理程序:
services.AddAuthentication() .AddScheme<JwtBearerOptions, IssuerFilteredJwtHandler>("Scheme1", options => { options.TokenValidationParameters = new TokenValidationParameters { ValidIssuer = "https://issuer1.com", // 其他验证参数 }; }, resolver => new IssuerFilteredJwtHandler( resolver.GetRequiredService<IOptionsMonitor<JwtBearerOptions>>(), resolver.GetRequiredService<ILoggerFactory>(), resolver.GetRequiredService<UrlEncoder>(), resolver.GetRequiredService<ISystemClock>(), "https://issuer1.com" )) .AddScheme<JwtBearerOptions, IssuerFilteredJwtHandler>("Scheme2", options => { options.TokenValidationParameters = new TokenValidationParameters { ValidIssuer = "https://issuer2.com", // 其他验证参数 }; }, resolver => new IssuerFilteredJwtHandler( resolver.GetRequiredService<IOptionsMonitor<JwtBearerOptions>>(), resolver.GetRequiredService<ILoggerFactory>(), resolver.GetRequiredService<UrlEncoder>(), resolver.GetRequiredService<ISystemClock>(), "https://issuer2.com" ));
内容的提问来源于stack exchange,提问作者Josip Juros
相关产品推荐
相关产品推荐

