自定义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
相关产品推荐
相关产品推荐

