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

