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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 16:44:56