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

如何在Spring Security 6的JWT认证中自定义错误处理?

自定义JWT资源服务器的401错误响应

要解决空白401响应的问题,需要分别处理token缺失/未触发认证和JWT验证失败两类场景,通过自定义两个处理器并配置到Spring Security中实现。

1. 自定义AuthenticationEntryPoint(处理token缺失等未认证场景)

这个类负责处理请求未携带token、token格式不符合Bearer规范等未触发JWT验证流程的情况:

@Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        
        String message = "Unauthorized access";
        if (authException instanceof MissingBearerTokenException) {
            message = "Access token missing";
        }
        
        ObjectMapper mapper = new ObjectMapper();
        mapper.writeValue(response.getOutputStream(), Collections.singletonMap("error", message));
    }
}

2. 自定义BearerTokenAuthenticationFailureHandler(处理JWT验证失败场景)

这个类负责捕获JWT验证过程中的各类异常(过期、无效、格式错误等),返回对应错误信息:

@Component
public class CustomBearerTokenFailureHandler implements BearerTokenAuthenticationFailureHandler {
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException {
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        
        String message = "Authentication failed";
        if (exception instanceof JwtExpiredException) {
            message = "Access token expired";
        } else if (exception instanceof InvalidJwtException) {
            message = "Invalid access token";
        } else if (exception instanceof JwtMalformedException) {
            message = "Malformed access token";
        }
        
        ObjectMapper mapper = new ObjectMapper();
        mapper.writeValue(response.getOutputStream(), Collections.singletonMap("error", message));
    }
}

3. 修改SecurityConfig配置,关联自定义处理器

将上述两个处理器配置到资源服务器的安全链中,替换默认的错误处理逻辑:

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    private final CustomAuthenticationEntryPoint customAuthEntryPoint;
    private final CustomBearerTokenFailureHandler customBearerFailureHandler;

    public SecurityConfig(CustomAuthenticationEntryPoint customAuthEntryPoint,
                         CustomBearerTokenFailureHandler customBearerFailureHandler) {
        this.customAuthEntryPoint = customAuthEntryPoint;
        this.customBearerFailureHandler = customBearerFailureHandler;
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .authorizeHttpRequests(authz -> authz
                        .requestMatchers("/public/**").permitAll()
                        .anyRequest().authenticated()
                )
                .oauth2ResourceServer(oauth2 -> oauth2
                        .jwt(Customizer.withDefaults())
                        .authenticationEntryPoint(customAuthEntryPoint)
                        .bearerTokenAuthenticationFilter(customBearerTokenFilter())
                );
        return http.build();
    }

    @Bean
    public BearerTokenAuthenticationFilter customBearerTokenFilter() {
        BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(authenticationManager());
        filter.setAuthenticationFailureHandler(customBearerFailureHandler);
        return filter;
    }

    @Bean
    public AuthenticationManager authenticationManager() {
        return new ProviderManager(Collections.singletonList(jwtAuthenticationProvider()));
    }

    @Bean
    public JwtAuthenticationProvider jwtAuthenticationProvider() {
        return new JwtAuthenticationProvider(jwtDecoder());
    }

    @Bean
    public JwtDecoder jwtDecoder() {
        // 根据你的认证服务配置实现,示例:
        // 对称密钥方式:
        // SecretKey secretKey = new SecretKeySpec("your-secret-key".getBytes(), SignatureAlgorithm.HS256.getJcaName());
        // return NimbusJwtDecoder.withSecretKey(secretKey).build();
        // JWKS方式:
        // return JwtDecoders.fromIssuerLocation("https://your-auth-server/.well-known/jwks.json");
        throw new UnsupportedOperationException("请配置符合你场景的JwtDecoder");
    }
}

关键说明

  • 之前尝试失败的原因:默认情况下,JWT验证失败的异常由BearerTokenAuthenticationFilter的默认处理器处理,而非通用的AuthenticationFailureHandler;必须自定义该过滤器的失败处理器才能捕获JWT相关异常。
  • 可以根据业务需求扩展更多异常类型(如InvalidClaimException),添加对应的错误信息。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 10:17:51