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

Spring Boot Security除成功请求外均返回403错误求助

问题排查与解决方案

核心原因

你的问题源于Spring Security拦截优先级高于Spring MVC的异常/错误处理:

  • 当出现404(URL不存在)、400(请求体无效)这类错误时,Spring MVC还未处理到控制器逻辑,Spring Security就先对请求做了权限校验。由于这些请求不在白名单内,且未携带有效JWT,Security直接返回403,覆盖了原本应返回的4xx状态码。
  • Spring Boot默认的错误处理端点/error被Security拦截,导致错误无法被正确渲染。

具体修复步骤

1. 放行错误处理端点

修改SecurityFilterChain配置,将/error加入白名单,允许所有请求访问:

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
    httpSecurity
            .csrf(csrf -> csrf.disable()) // 原配置中先设置仓库再disable属于冗余操作,直接禁用即可
            .authorizeHttpRequests(authorize -> authorize
                    .requestMatchers("/api/v1/*/public/**", "/api/v1/auth/login", "/error")
                    .permitAll()
                    .anyRequest().authenticated())
            .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
    return httpSecurity.build();
}

2. 配置Security异常处理

添加异常处理器,让认证失败、权限不足等场景返回对应状态码,不干扰Spring MVC的其他错误处理:

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
    httpSecurity
            // 保留原有配置
            .csrf(csrf -> csrf.disable())
            .authorizeHttpRequests(authorize -> authorize
                    .requestMatchers("/api/v1/*/public/**", "/api/v1/auth/login", "/error")
                    .permitAll()
                    .anyRequest().authenticated())
            .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class)
            // 新增异常处理配置
            .exceptionHandling(exception -> exception
                    .authenticationEntryPoint((request, response, authException) -> {
                        // 未认证时返回401,替代默认的403
                        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "未认证");
                    })
                    .accessDeniedHandler((request, response, accessDeniedException) -> {
                        // 权限不足时返回403
                        response.sendError(HttpServletResponse.SC_FORBIDDEN, "权限不足");
                    }));
    return httpSecurity.build();
}

3. 完善JwtRequestFilter逻辑(必要补充)

你当前的JwtRequestFilter仅传递请求,未实现JWT校验逻辑,会导致非白名单请求即使无有效JWT也会被放行到后续环节,最终仍被Security拦截返回403。建议补充校验逻辑:

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
        throws ServletException, IOException {
    // 从请求头提取JWT Token
    String authHeader = request.getHeader("Authorization");
    String jwtToken = null;
    String username = null;

    if (authHeader != null && authHeader.startsWith("Bearer ")) {
        jwtToken = authHeader.substring(7);
        // 解析JWT获取用户名(替换为你的签名密钥和解析逻辑)
        try {
            username = Jwts.parserBuilder()
                    .setSigningKey("your-jwt-secret-key") // 替换成实际的JWT签名密钥
                    .build()
                    .parseClaimsJws(jwtToken)
                    .getBody()
                    .getSubject();
        } catch (Exception e) {
            // Token无效时直接返回401
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "无效的Token");
            return;
        }
    }

    // 若用户名存在且当前未认证,设置认证上下文
    if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
        UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
                username, null, Collections.emptyList());
        authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
        SecurityContextHolder.getContext().setAuthentication(authToken);
    }

    chain.doFilter(request, response);
}

验证效果

完成配置后:

  • 访问不存在的URL返回404
  • 请求体无效返回400
  • 未携带有效JWT访问需认证接口返回401
  • 携带无效JWT返回401
  • 权限不足返回403
  • 合法请求正常处理

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 12:57:47