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

自定义ActionFilter结合JwtBearerEvents实现JWT认证日志问题

问题根源与解决思路

你当前的核心问题是自定义ActionFilter完全绕开了ASP.NET Core内置的JWT Bearer认证中间件,导致配置的CustomJwtBearerEvents根本不会被触发——因为中间件从未参与到你的认证流程中。下面分两种场景给出具体解决方案:


方案一:改用官方JWT认证流程(推荐)

如果要保留JwtBearerEvents的日志能力,最佳方式是依托ASP.NET Core内置的认证体系,同时可以整合你自定义的验证逻辑。

1. 修正基础代码的语法错误

首先修复你的AuthenticationService定义(原代码把类写成了接口,属于语法错误):

public interface IAuthenticationService
{
    // 补充token参数,匹配调用逻辑
    bool ValidateTokenOrThrow(string token);
}

public class AuthenticationService : IAuthenticationService
{
    public bool ValidateTokenOrThrow(string token)
    {
        // 你的JWT验证逻辑(示例)
        var handler = new JwtSecurityTokenHandler();
        var validationParams = new TokenValidationParameters
        {
            // 配置你的验证参数:Issuer、Audience、SigningKey等
            ValidIssuer = "你的签发者",
            ValidAudience = "你的受众",
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("你的密钥"))
        };

        try
        {
            handler.ValidateToken(token, validationParams, out _);
            return true;
        }
        catch
        {
            throw new SecurityTokenException("无效令牌");
        }
    }
}

2. 修正自定义JwtBearerEvents

修正方法重写的语法错误,并添加日志逻辑:

public class CustomJwtBearerEvents : JwtBearerEvents
{
    private readonly ILogger<CustomJwtBearerEvents> _logger;
    private readonly IWebHostEnvironment _env;

    public CustomJwtBearerEvents(ILogger<CustomJwtBearerEvents> logger, IWebHostEnvironment env)
    {
        _logger = logger;
        _env = env;
    }

    // 认证失败时触发
    public override Task OnAuthenticationFailed(AuthenticationFailedContext context)
    {
        _logger.LogError(context.Exception, "JWT认证失败");
        
        context.Response.StatusCode = StatusCodes.Status401Unauthorized;
        context.Response.ContentType = "application/json";
        var errMsg = _env.IsDevelopment() ? context.Exception.ToString() : "认证处理出错";
        var result = JsonConvert.SerializeObject(new { err = errMsg });
        
        return context.Response.WriteAsync(result);
    }

    // 认证成功时触发
    public override Task OnTokenValidated(TokenValidatedContext context)
    {
        var userId = context.Principal?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        _logger.LogInformation("JWT认证成功,用户ID: {UserId}", userId);
        return base.OnTokenValidated(context);
    }
}

3. 正确配置Startup

注册服务并启用官方认证流程:

public void ConfigureServices(IServiceCollection services)
{
    // 注册自定义认证服务
    services.AddScoped<IAuthenticationService, AuthenticationService>();
    // 注册自定义JWT事件
    services.AddScoped<CustomJwtBearerEvents>();

    services.AddAuthorization();
    services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(options =>
        {
            // 配置JWT验证参数(和AuthenticationService保持一致)
            options.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidateLifetime = true,
                ValidateIssuerSigningKey = true,
                ValidIssuer = "你的签发者",
                ValidAudience = "你的受众",
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("你的密钥"))
            };
            // 指定使用自定义事件类
            options.EventsType = typeof(CustomJwtBearerEvents);
        });

    services.AddControllers();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // 其他中间件(如异常处理、静态文件)...
    
    // 必须添加认证和授权中间件,顺序不能错
    app.UseAuthentication();
    app.UseAuthorization();

    app.UseEndpoints(endpoints => endpoints.MapControllers());
}

4. 替换ActionFilter为官方Authorize属性

移除自定义的CustomActionAttribute,改用[Authorize]触发官方认证流程:

public class HomeController : ControllerBase
{
    [Authorize]
    [Route("api/[controller]")]
    public IActionResult GetHome() 
    { 
       // 你的业务逻辑
       return Ok(); 
    }
}

方案二:保留自定义ActionFilter(手动实现日志)

如果必须保留自定义ActionFilter的认证方式,那么JwtBearerEvents无法生效,需要在ActionFilter内手动实现日志逻辑:

public class CustomActionAttribute : ActionFilterAttribute
{
    private readonly IAuthenticationService _authenticationService;
    private readonly ILogger<CustomActionAttribute> _logger;
    private readonly IWebHostEnvironment _env;

    public CustomActionAttribute(IAuthenticationService authenticationService, ILogger<CustomActionAttribute> logger, IWebHostEnvironment env)
    {
        _authenticationService = authenticationService;
        _logger = logger;
        _env = env;
    }

    public override async Task OnActionExecutingAsync(ActionExecutingContext context, CancellationToken cancellationToken)
    {
        if (!context.HttpContext.Request.Headers.TryGetValue("Authorization", out var headerValue))
        {
            _logger.LogWarning("请求缺少Authorization头");
            context.Result = new UnauthorizedResult();
            return;
        }

        var token = headerValue.ToString().Replace("Bearer ", "");
        try
        {
            _authenticationService.ValidateTokenOrThrow(token);
            _logger.LogInformation("认证成功");
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "认证失败");
            var errMsg = _env.IsDevelopment() ? ex.ToString() : "认证处理出错";
            context.Result = new JsonResult(new { err = errMsg }) { StatusCode = StatusCodes.Status401Unauthorized };
            await Task.CompletedTask;
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 13:15:24