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

Spring Boot 3中JWT认证失败如何返回自定义错误信息?

解决Spring Boot 3中JWT认证自定义异常信息不生效问题

问题现象

在Spring Boot 3环境下实现JWT认证时,当Token验证失败抛出自定义TokenException后,客户端始终收到固定提示"Full authentication is required to access this resource",而非TokenException中定义的自定义错误信息。

问题原因

核心原因是自定义异常未被正确传递到Spring Security的AuthenticationEntryPoint:

  • 若TokenAuthenticationFilter中捕获了TokenException但未正确委托给AuthenticationEntryPoint处理,Spring Security会触发默认的认证失败逻辑,返回固定提示。
  • 部分场景下,异常可能被Spring Security包装为其他AuthenticationException子类,导致无法直接获取自定义消息。

解决方案

1. 修正TokenAuthenticationFilter的异常处理逻辑

确保在Filter中抛出TokenException后,直接调用自定义的AuthenticationEntryPoint处理异常,避免异常被Spring Security默认拦截。示例代码:

@Component
public class TokenAuthenticationFilter extends OncePerRequestFilter {

    private final JwtTokenProvider jwtTokenProvider;
    private final AuthenticationEntryPoint restAuthenticationEntryPoint;

    // 构造注入依赖
    public TokenAuthenticationFilter(JwtTokenProvider jwtTokenProvider, AuthenticationEntryPoint restAuthenticationEntryPoint) {
        this.jwtTokenProvider = jwtTokenProvider;
        this.restAuthenticationEntryPoint = restAuthenticationEntryPoint;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        try {
            String authToken = extractTokenFromRequest(request);
            if (authToken != null && !authToken.isBlank()) {
                // 调用JWT验证方法,失败则抛出TokenException
                jwtTokenProvider.decodeJWT(authToken);
                // 验证成功后设置认证信息到SecurityContext(根据自身逻辑实现)
                setAuthenticationToContext(request);
            }
            filterChain.doFilter(request, response);
        } catch (TokenException e) {
            // 清除上下文,避免残留认证信息
            SecurityContextHolder.clearContext();
            // 委托自定义AuthenticationEntryPoint处理异常
            restAuthenticationEntryPoint.commence(request, response, e);
        }
    }

    // 辅助方法:从请求头中提取Token(示例逻辑)
    private String extractTokenFromRequest(HttpServletRequest request) {
        String bearerToken = request.getHeader("Authorization");
        if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7);
        }
        return null;
    }

    // 辅助方法:设置认证信息到SecurityContext(根据自身业务实现)
    private void setAuthenticationToContext(HttpServletRequest request) {
        // 示例逻辑,需替换为实际的用户认证逻辑
        Authentication authentication = new UsernamePasswordAuthenticationToken("username", null, Collections.emptyList());
        SecurityContextHolder.getContext().setAuthentication(authentication);
    }
}

2. 优化AuthenticationEntryPoint的异常消息处理

在自定义的restAuthenticationEntryPoint中,直接判断异常类型,确保能获取到TokenException的自定义消息:

@Bean
public AuthenticationEntryPoint restAuthenticationEntryPoint(ObjectMapper objectMapper) {
    return (request, response, error) -> {
        log.info("restAuthenticationEntryPoint: {}", error.getMessage(), error);
        Map<String, Object> errorObject = new HashMap<>();
        int errorCode = HttpStatus.UNAUTHORIZED.value();
        
        // 优先使用自定义TokenException的消息
        String errorMessage = error.getMessage();
        if (error instanceof TokenException) {
            errorMessage = error.getMessage();
        } else {
            // 非自定义异常时返回默认提示
            errorMessage = "Full authentication is required to access this resource";
        }

        errorObject.put("message", errorMessage);
        errorObject.put("error", HttpStatus.UNAUTHORIZED.getReasonPhrase());
        errorObject.put("code", errorCode);
        errorObject.put("timestamp", new Timestamp(System.currentTimeMillis()));
        
        response.setContentType("application/json;charset=UTF-8");
        response.setStatus(errorCode);
        response.getWriter().write(objectMapper.writeValueAsString(errorObject));
    };
}

3. 确保SecurityFilterChain配置正确

保持原有的SecurityFilterChain配置,但需确保TokenAuthenticationFilter被正确注入并添加到过滤器链中:

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http, TokenAuthenticationFilter tokenAuthenticationFilter) throws Exception {
    http
            .csrf(AbstractHttpConfigurer::disable)
            .formLogin(AbstractHttpConfigurer::disable)
            .httpBasic(AbstractHttpConfigurer::disable)
            .exceptionHandling(e -> e.authenticationEntryPoint(restAuthenticationEntryPoint(objectMapper)))
            .sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .addFilterBefore(tokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
 
    return http.build();
}

验证

完成上述修改后,测试不同的Token异常场景(过期、签名错误等),客户端将收到对应的自定义错误消息,而非固定提示。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 05:15:29