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

Spring Security资源服务器(Servlet栈)JWT无效令牌日志记录咨询

基于Servlet栈的Spring Security资源服务器无效JWT日志与监控方案

核心结论

完全支持记录WARN/ERROR级别的无效JWT日志,无需依赖DEBUG/TRACE级别日志,同时可以通过自定义逻辑实现符合OWASP API10要求的监控能力。


方案1:自定义JWT认证失败处理器

通过实现AuthenticationFailureHandler,精准捕获JWT相关认证失败场景,记录指定级别的日志,并区分失败原因:

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.oauth2.jwt.InvalidJwtException;

import java.io.IOException;

public class CustomJwtFailureHandler implements AuthenticationFailureHandler {
    private static final Logger logger = LoggerFactory.getLogger(CustomJwtFailureHandler.class);

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        // 区分不同的JWT无效场景
        if (exception instanceof InvalidJwtException) {
            InvalidJwtException jwtException = (InvalidJwtException) exception;
            // 针对签名无效、过期、格式错误等细分场景记录日志
            logger.warn("Invalid JWT token received for request {}: {}", request.getRequestURI(), jwtException.getLocalizedMessage());
        } else if (exception instanceof BadCredentialsException) {
            logger.warn("Invalid credentials for request: {}", request.getRequestURI());
        } else {
            logger.error("Unexpected authentication failure for request: {}", request.getRequestURI(), exception);
        }
        
        // 返回标准401响应
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid or unauthorized token");
    }
}

在SecurityFilterChain配置中绑定该处理器:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;

@Configuration
public class ResourceServerConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .oauth2ResourceServer(oauth2 -> oauth2
                .jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter()))
                .authenticationFailureHandler(customJwtFailureHandler())
            );
        return http.build();
    }

    @Bean
    public AuthenticationFailureHandler customJwtFailureHandler() {
        return new CustomJwtFailureHandler();
    }

    // 配置JWT转换器等其他必要组件
    // ...
}

方案2:监听Spring Security认证事件

通过Spring事件监听机制,无侵入式捕获AuthenticationFailureEvent,实现日志记录:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.event.EventListener;
import org.springframework.security.authentication.event.AuthenticationFailureEvent;
import org.springframework.security.oauth2.jwt.InvalidJwtException;
import org.springframework.stereotype.Component;

@Component
public class AuthFailureEventListener {
    private static final Logger logger = LoggerFactory.getLogger(AuthFailureEventListener.class);

    @EventListener
    public void handleAuthFailure(AuthenticationFailureEvent event) {
        Exception exception = event.getException();
        if (exception instanceof InvalidJwtException) {
            InvalidJwtException jwtException = (InvalidJwtException) exception;
            logger.warn("JWT validation failed: {}", jwtException.getMessage());
            // 可在此处扩展监控逻辑
        }
    }
}

方案3:结合监控工具满足OWASP API10要求

为监控无效JWT拒绝数量的上升趋势,可结合Micrometer添加自定义指标:

import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import jakarta.annotation.PostConstruct;
import org.springframework.stereotype.Component;

@Component
public class JwtMetrics {
    private final MeterRegistry meterRegistry;
    private Counter invalidJwtCounter;

    public JwtMetrics(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
    }

    @PostConstruct
    public void init() {
        invalidJwtCounter = Counter.builder("invalid.jwt.tokens")
                .description("Number of invalid JWT tokens rejected")
                .tag("failure_reason", "unknown")
                .register(meterRegistry);
    }

    public void incrementInvalidJwt(String failureReason) {
        invalidJwtCounter.tag("failure_reason", failureReason).increment();
    }
}

在自定义失败处理器中调用指标统计:

// 注入JwtMetrics
private final JwtMetrics jwtMetrics;

public CustomJwtFailureHandler(JwtMetrics jwtMetrics) {
    this.jwtMetrics = jwtMetrics;
}

// 在onAuthenticationFailure方法中
if (exception instanceof InvalidJwtException) {
    // ...日志记录
    String reason = extractFailureReason((InvalidJwtException) exception);
    jwtMetrics.incrementInvalidJwt(reason);
}

// 自定义方法提取细分失败原因
private String extractFailureReason(InvalidJwtException exception) {
    if (exception.getMessage().contains("signature")) {
        return "signature_invalid";
    } else if (exception.getMessage().contains("expired")) {
        return "token_expired";
    }
    return "invalid_format";
}

通过Prometheus采集指标后,可在Grafana中创建仪表盘监控无效JWT数量变化,并设置阈值告警,满足OWASP API10的日志与监控要求。


为什么不推荐DEBUG/TRACE级别日志

生产环境开启DEBUG/TRACE会产生大量无关日志,占用存储资源且增加分析复杂度。上述方案仅针对无效JWT场景记录必要的WARN/ERROR级别日志,同时保留细分失败原因的能力,更适合生产环境。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 03:35:11