如何在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
相关产品推荐
相关产品推荐

