如何用Spring Boot Actuator获取OAuth端点HTTP追踪及JWT验证耗时?
解决方案
方法一:扩展Actuator的HttpExchange追踪范围
从Spring Boot 2.4起,/httptrace端点被/actuator/httpexchanges替代,默认仅追踪经过DispatcherServlet的请求,而Spring Security的OAuth端点(/authorize、/token)由Filter链处理、不走DispatcherServlet,因此需要自定义配置捕获这类请求:
- 自定义Servlet Filter,拦截所有请求并提交到
HttpExchangeRepository:
import org.springframework.boot.actuate.web.exchanges.HttpExchange; import org.springframework.boot.actuate.web.exchanges.HttpExchangeRepository; import org.springframework.boot.web.servlet.filter.OrderedFilter; import org.springframework.stereotype.Component; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @Component public class AllRequestTraceFilter implements Filter, OrderedFilter { private final HttpExchangeRepository repository; public AllRequestTraceFilter(HttpExchangeRepository repository) { this.repository = repository; } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { if (request instanceof HttpServletRequest && response instanceof HttpServletResponse) { HttpServletRequest httpRequest = (HttpServletRequest) request; HttpServletResponse httpResponse = (HttpServletResponse) response; long start = System.currentTimeMillis(); try { chain.doFilter(request, response); } finally { long duration = System.currentTimeMillis() - start; this.repository.add(new HttpExchange(httpRequest, httpResponse, duration, null)); } } else { chain.doFilter(request, response); } } // 设置Filter顺序为最高优先级,确保在Spring Security过滤器前捕获请求 @Override public int getOrder() { return OrderedFilter.HIGHEST_PRECEDENCE; } }
- 启用Actuator的
httpexchanges端点:
在application.yml中配置:
management: endpoints: web: exposure: include: httpexchanges endpoint: httpexchanges: enabled: true
访问/actuator/httpexchanges即可查看包含OAuth端点在内的所有请求追踪,其中包含请求耗时数据。
方法二:用Micrometer监控JWT验证耗时
通过Micrometer自定义Timer,精准统计JWT令牌验证的时间:
- 自定义计时版的BearerToken过滤器:
import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Timer; import org.springframework.security.web.authentication.www.BearerTokenAuthenticationFilter; import org.springframework.stereotype.Component; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @Component public class TimedBearerTokenFilter extends BearerTokenAuthenticationFilter { private final Timer timer; public TimedBearerTokenFilter(MeterRegistry registry) { super(null); // 根据实际项目的构造参数调整 this.timer = Timer.builder("jwt.validation.duration") .description("JWT令牌验证耗时统计") .register(registry); } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { timer.record(() -> { try { super.doFilterInternal(request, response, filterChain); } catch (ServletException | IOException e) { throw new RuntimeException(e); } }); } }
- 在Security配置中替换默认过滤器:
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.www.BasicAuthenticationFilter; @Configuration public class SecurityConfig { private final TimedBearerTokenFilter timedBearerTokenFilter; public SecurityConfig(TimedBearerTokenFilter timedBearerTokenFilter) { this.timedBearerTokenFilter = timedBearerTokenFilter; } @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth.anyRequest().authenticated()) .oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt -> jwt)) .addFilterBefore(timedBearerTokenFilter, BasicAuthenticationFilter.class); return http.build(); } }
- 访问
/actuator/metrics/jwt.validation.duration,即可查看JWT验证的耗时统计(平均值、最大值等维度数据)。
方法三:自定义日志记录耗时
如果不需要集成到Actuator,可直接通过日志输出JWT验证时间:
- 包装JwtDecoder添加计时逻辑:
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.jwt.JwtDecoder; import org.springframework.stereotype.Component; @Component public class TimedJwtDecoder implements JwtDecoder { private final JwtDecoder delegate; private final Logger logger = LoggerFactory.getLogger(TimedJwtDecoder.class); public TimedJwtDecoder(JwtDecoder delegate) { this.delegate = delegate; } @Override public Jwt decode(String token) { long start = System.currentTimeMillis(); try { return delegate.decode(token); } finally { long duration = System.currentTimeMillis() - start; logger.info("JWT令牌验证耗时:{} ms", duration); } } }
- 在配置中使用自定义Decoder:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.oauth2.jwt.JwtDecoder; import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; @Configuration public class JwtConfig { @Bean public JwtDecoder jwtDecoder() { NimbusJwtDecoder decoder = NimbusJwtDecoder.withJwkSetUri("你的JWK地址").build(); return new TimedJwtDecoder(decoder); } }
每次JWT验证时,日志中会输出具体耗时。
内容的提问来源于stack exchange,提问作者saldanaj27
相关产品推荐
相关产品推荐

