You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Core Web API标注[Authorize]时收到无效Token如何记录日志

JWT无效Token场景日志记录实现方案

针对ASP.NET Core Web API中JwtBearer认证校验失败时提前返回401、无法进入控制器逻辑记录日志的问题,有两种成熟实现方式,可根据业务场景选择:


方案1:绑定JwtBearer内置事件(推荐,仅针对携带无效Token的场景)

JwtBearer认证中间件原生提供了事件钩子,其中OnAuthenticationFailed委托会在请求携带Token但校验全流程任意环节出错时触发,覆盖Token格式非法、签名无效、过期、签发者不匹配等所有无效Token场景,不会改变原有401返回逻辑,侵入性最低。

直接修改现有JwtBearer配置,追加事件处理即可:

builder.Services
    .AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.Configuration = openIdConfig;
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuerSigningKey = true,
            IssuerSigningKeyResolver = (string token, SecurityToken securityToken, string kid, TokenValidationParameters validationParameters) =>
            {
                // 生产环境建议替换为IHttpClientFactory异步拉取,避免.Result同步阻塞导致线程池性能问题
                var json = new HttpClient().GetStringAsync(openIdConfig.JwksUri).Result;
                var jsonWebkeySet = JsonWebKeySet.Create(json);
                return jsonWebkeySet.GetSigningKeys();
            },
            ValidateIssuer = true,
            ValidIssuer = tokenIssuer,
            ValidateLifetime = true,
            ValidateAudience = false
        };

        // 追加鉴权失败事件处理
        options.Events = new JwtBearerEvents
        {
            OnAuthenticationFailed = context =>
            {
                // 从请求服务容器获取日志实例
                var logger = context.HttpContext.RequestServices
                    .GetRequiredService<ILoggerFactory>()
                    .CreateLogger("JwtAuth");
                
                // 记录失败详情,可按需记录请求方法、请求头、用户代理等信息
                logger.LogWarning(context.Exception, 
                    "JWT校验失败,请求路径:{RequestPath},来源IP:{RemoteIP},错误信息:{ErrorMessage}",
                    context.HttpContext.Request.Path,
                    context.HttpContext.Connection.RemoteIpAddress?.ToString(),
                    context.Exception.Message);

                return Task.CompletedTask;
            }
        };
    });

该方案特点:可以拿到具体的校验异常堆栈,方便快速定位是过期、签名错还是签发者不匹配等具体问题,但不会捕获「请求完全未携带Token」导致的401场景。


方案2:自定义中间件全局记录所有401场景

如果需要记录所有返回401的请求(包括未传Token、Token无效等所有未授权场景),可以通过自定义中间件在响应返回阶段拦截状态码做日志记录。

注意中间件注册顺序必须放在认证、授权中间件之前:

var app = builder.Build();

// 先注册鉴权日志中间件
app.Use(async (context, next) =>
{
    // 先执行后续中间件逻辑
    await next();

    // 响应返回前判断状态码是否为401
    if (context.Response.StatusCode == StatusCodes.Status401Unauthorized)
    {
        var logger = context.RequestServices
            .GetRequiredService<ILoggerFactory>()
            .CreateLogger("AuthGlobal");
        
        // 提取请求携带的Token
        var hasAuthHeader = context.Request.Headers.TryGetValue("Authorization", out var authValues);
        var rawToken = hasAuthHeader 
            ? authValues.FirstOrDefault()?.Replace("Bearer ", string.Empty) 
            : null;

        logger.LogWarning(
            "请求返回401未授权,请求路径:{RequestPath},来源IP:{RemoteIP},携带Token:{TokenPreview}",
            context.Request.Path,
            context.Connection.RemoteIpAddress?.ToString(),
            // Token必须做脱敏处理,禁止明文打印完整Token避免凭证泄露
            string.IsNullOrEmpty(rawToken) ? "无" : $"{rawToken[..Math.Min(10, rawToken.Length)]}***"
        );
    }
});

// 后续再注册认证、授权、路由中间件
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();

app.Run();

注意事项

  • 所有涉及Token的日志必须做脱敏处理,禁止打印完整Token内容,避免认证凭证泄露
  • 原配置中IssuerSigningKeyResolver使用.Result同步拉取JWKS存在线程阻塞风险,生产环境建议直接使用JwtBearer默认的配置管理器自动拉取、缓存公钥,不需要手动实现公钥拉取逻辑
  • 如果需要在鉴权失败时自定义返回格式,也可以在上述两个扩展点中直接向context.HttpContext.Response写入自定义JSON内容,再调用context.HandleResponse()终止默认返回逻辑即可

内容的提问来源于stack exchange,提问作者chinju

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 18:51:23