如何为.NET Core WebAPI的JWT认证及授权失败添加自定义日志
解决.NET Core WebAPI中认证失败场景的自定义日志统计问题
我之前也碰到过一模一样的问题——你的IAsyncActionFilter属于MVC管道的一部分,只有当请求能顺利走到Action执行阶段才会触发,但认证失败的请求早在MVC管道启动前就被拦截了,所以根本碰不到你的过滤器。结合你提到的JwtBearerEvents和AuthorizeFilter的问题,我给你几个实用的解决方案:
方案一:利用JwtBearerEvents覆盖全场景认证事件
你之前说JwtBearerEvents没生效,大概率是没覆盖到所有关键场景。除了AuthenticationFailed,还要处理不带Token和认证挑战的情况,修改你的JWT配置代码如下:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.Authority = Configuration["Authority"]; options.Audience = "myAudience"; options.Events = new JwtBearerEvents { // 捕获完全不带Token的请求 OnMessageReceived = context => { if (string.IsNullOrEmpty(context.Request.Headers["Authorization"])) { // 这里记录StatsD统计,比如"api.auth.no_token"计数+1 // 注意:如果StatsD客户端是注入的,要通过context.HttpContext.RequestServices获取实例 var statsd = context.HttpContext.RequestServices.GetService<IStatsdClient>(); statsd?.Increment("api.auth.no_token"); } return Task.CompletedTask; }, // 捕获Token无效、过期等认证失败场景 OnAuthenticationFailed = context => { var statsd = context.HttpContext.RequestServices.GetService<IStatsdClient>(); statsd?.Increment("api.auth.failed"); // 可选:记录具体错误信息 // _logger.LogError(context.Exception, "JWT认证失败"); return Task.CompletedTask; }, // 捕获认证失败后返回401挑战的场景 OnChallenge = context => { var statsd = context.HttpContext.RequestServices.GetService<IStatsdClient>(); statsd?.Increment("api.auth.challenged"); // 如果你需要自定义401响应,记得调用context.HandleResponse(),否则会用默认响应 return Task.CompletedTask; } }; });
方案二:添加全局自定义中间件,统一捕获所有认证失败响应
如果想统一处理所有认证类型的失败(不止JWT),可以在UseAuthentication之后、UseMvc之前加一个中间件,在请求结束后检查响应状态:
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { // ...其他中间件 app.UseAuthentication(); // 自定义认证统计中间件 app.Use(async (context, next) => { await next(); // 检查是否返回401未授权 if (context.Response.StatusCode == StatusCodes.Status401Unauthorized) { var authenticateResult = await context.AuthenticateAsync(JwtBearerDefaults.AuthenticationScheme); var statsd = context.RequestServices.GetService<IStatsdClient>(); if (!authenticateResult.Succeeded) { if (authenticateResult.Failure != null) { // Token无效/过期等带异常的失败 statsd?.Increment("api.auth.failure"); } else if (authenticateResult.None) { // 完全无Token的情况 statsd?.Increment("api.auth.no_token"); } } } }); app.UseMvc(); // ...其他中间件 }
这个方案能覆盖所有返回401的场景,不管是认证中间件直接拦截的,还是[Authorize]触发的。
方案三:全局添加IAuthorizationFilter处理授权失败
如果还要统计权限不足(Token有效但无对应权限)的场景,可以添加一个全局授权过滤器:
public class AuthorizationStatsFilter : IAuthorizationFilter { private readonly IStatsdClient _statsdClient; public AuthorizationStatsFilter(IStatsdClient statsdClient) { _statsdClient = statsdClient; } public void OnAuthorization(AuthorizationFilterContext context) { if (context.Result is UnauthorizedResult) { // 认证失败触发的401 _statsdClient.Increment("api.auth.unauthorized"); } else if (context.Result is ForbidResult) { // 权限不足触发的403 _statsdClient.Increment("api.auth.forbidden"); } } }
然后在ConfigureServices里注册为全局过滤器:
services.AddMvc(options => { options.Filters.Add<AuthorizationStatsFilter>(); });
总结推荐
- 只针对JWT认证场景:用方案一即可;
- 需要覆盖所有认证类型:方案二更通用;
- 要统计权限不足的情况:搭配方案三使用。
内容的提问来源于stack exchange,提问作者Ewan
相关产品推荐
相关产品推荐

