Spring Boot:JwtValidationException未触发自定义认证入口类的解决方法
解决JwtValidationException无法触发自定义认证入口点的问题
问题根源
Spring Security处理JWT验证时,JwtValidationException属于令牌验证阶段抛出的异常,默认不会被你配置的CustomOAuth2AuthenticationEntryPoint捕获,而是由框架直接返回无额外信息的401响应。要让它走自定义异常处理逻辑,有两种可行方案:
方案一:自定义JwtAuthenticationProvider转换异常
通过重写JwtAuthenticationProvider,在验证JWT时捕获JwtValidationException,并将其包装为Spring Security认证流程能识别的AuthenticationException子类,这样就能触发你的CustomOAuth2AuthenticationEntryPoint。
1. 实现自定义认证Provider
public class CustomJwtAuthenticationProvider extends JwtAuthenticationProvider { public CustomJwtAuthenticationProvider(JwtDecoder jwtDecoder) { super(jwtDecoder); } @Override protected Authentication authenticate(Authentication authentication) throws AuthenticationException { try { return super.authenticate(authentication); } catch (JwtValidationException e) { // 根据异常类型生成对应错误信息,再包装为BadCredentialsException throw new BadCredentialsException(getValidationMsg(e), e); } } private String getValidationMsg(JwtValidationException e) { if (e.getCause() instanceof ExpiredJwtException) { return "令牌已过期"; } else if (e.getCause() instanceof SignatureException) { return "令牌签名无效"; } else { return "令牌验证失败:" + e.getMessage(); } } }
2. 在Security配置中替换默认Provider
修改原有的SecurityFilterChain配置,将自定义Provider注入到JWT配置中:
@Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .cors().configurationSource(corsConfigurationSource).and() .csrf().disable() .httpBasic(Customizer.withDefaults()) .sessionManagement((session) -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .exceptionHandling((exceptions) -> exceptions .authenticationEntryPoint(new CustomOAuth2AuthenticationEntryPoint()) .accessDeniedHandler(new CustomOAuth2AccessDeniedHandler()) ) .oauth2ResourceServer(oauth2 -> oauth2 .jwt(jwt -> jwt .jwtAuthenticationConverter(authenticationConverter()) // 替换为自定义的认证Provider .authenticationProvider(new CustomJwtAuthenticationProvider(jwtDecoder())) ) ); return http.build(); } // 确保已定义JwtDecoder Bean(示例为Nimbus实现) @Bean public JwtDecoder jwtDecoder() { return NimbusJwtDecoder.withJwkSetUri("你的JWKS地址").build(); }
这样,JWT验证失败时抛出的JwtValidationException会被转换为BadCredentialsException,进而触发你的自定义认证入口点,返回带详情的JSON响应。
方案二:全局异常处理器直接捕获
如果不想修改认证流程,也可以通过@RestControllerAdvice实现全局异常捕获,直接处理JwtValidationException:
@RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(JwtValidationException.class) public ResponseEntity<Map<String, Object>> handleJwtValidationException(JwtValidationException e) { Map<String, Object> response = new HashMap<>(); response.put("status", HttpStatus.UNAUTHORIZED.value()); response.put("message", getValidationMsg(e)); response.put("timestamp", LocalDateTime.now()); return new ResponseEntity<>(response, HttpStatus.UNAUTHORIZED); } private String getValidationMsg(JwtValidationException e) { if (e.getCause() instanceof ExpiredJwtException) { return "令牌已过期"; } else if (e.getCause() instanceof SignatureException) { return "令牌签名无效"; } else { return "令牌验证失败:" + e.getMessage(); } } }
这种方式无需修改Security配置,直接在全局层面捕获异常并返回定制响应,适合不需要统一走认证入口点逻辑的场景。
注意事项
- 两种方案二选一即可:方案一更贴合Spring Security认证流程,适合需要统一处理所有认证异常的场景;方案二更简洁,适合单独处理JWT验证异常的需求。
内容的提问来源于stack exchange,提问作者Plaul
相关产品推荐
相关产品推荐

