Spring Boot接口返回403而非405 Method Not Allowed问题修复
解决Spring Boot接口返回403而非405的问题
问题根源在于Spring Security的CSRF过滤器执行优先级高于Spring MVC的请求方法校验:当启用CSRF保护时,POST请求会先被Security拦截检查令牌,若令牌缺失直接返回403,根本没机会进入MVC环节判断请求方法是否允许,因此看不到预期的405。
以下是两种可行的解决思路:
方案一:对特定接口忽略CSRF检查
如果你的接口是纯后端调用(无需浏览器端CSRF保护),可以直接在Security配置中跳过指定路径的CSRF校验,让MVC的方法校验优先执行:
@Configuration public class ApiSecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http // 保留你的其他安全配置(如认证、授权规则) .authorizeHttpRequests(auth -> auth .anyRequest().authenticated() ) .csrf(csrf -> csrf // 忽略不需要CSRF保护的接口路径,支持Ant风格匹配 .ignoringRequestMatchers("/api/public/**", "/api/third-party/**") ) .exceptionHandling(ex -> ex .authenticationEntryPoint(new CustomAuthenticationEntryPoint()) ); return http.build(); } }
配置后,这些被忽略的接口在收到不支持的请求方法时,会直接返回405而非403。
方案二:添加前置过滤器优先校验请求方法
如果所有接口都需要CSRF保护,可自定义一个过滤器放在CSRF过滤器之前,先检查当前请求的路径是否支持对应的HTTP方法,不支持则直接返回405:
1. 实现自定义方法校验过滤器
@Component public class RequestMethodValidationFilter extends OncePerRequestFilter { private final RequestMappingHandlerMapping requestMappingHandlerMapping; private final PathMatcher pathMatcher = new AntPathMatcher(); public RequestMethodValidationFilter(RequestMappingHandlerMapping requestMappingHandlerMapping) { this.requestMappingHandlerMapping = requestMappingHandlerMapping; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String requestUri = request.getRequestURI(); String requestMethod = request.getMethod(); try { // 查找当前路径对应的所有支持的HTTP方法 Set<String> allowedMethods = requestMappingHandlerMapping.getHandlerMethods().entrySet().stream() .filter(entry -> entry.getKey().getPatternsCondition().getPatterns().stream() .anyMatch(pattern -> pathMatcher.match(pattern, requestUri))) .flatMap(entry -> entry.getKey().getMethodsCondition().getMethods().stream()) .map(HttpMethod::name) .collect(Collectors.toSet()); // 如果请求方法不在允许范围内,直接返回405 if (!allowedMethods.isEmpty() && !allowedMethods.contains(requestMethod)) { response.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED); response.setContentType(MediaType.APPLICATION_JSON_VALUE); // 可根据你的全局异常格式自定义返回内容 response.getWriter().write("{\"code\":405,\"message\":\"不支持的请求方法,允许的方法:" + String.join(",", allowedMethods) + "\"}"); return; } } catch (Exception e) { // 异常时放行,继续执行后续过滤器 filterChain.doFilter(request, response); return; } // 方法合法,继续执行CSRF等后续过滤器 filterChain.doFilter(request, response); } }
2. 在Security配置中注册前置过滤器
@Configuration public class ApiSecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http, RequestMethodValidationFilter methodValidationFilter) throws Exception { http // 将自定义过滤器放在CSRF过滤器之前 .addFilterBefore(methodValidationFilter, CsrfFilter.class) // 保留原有安全配置 .authorizeHttpRequests(auth -> auth .anyRequest().authenticated() ) .csrf(csrf -> csrf // 保留你的CSRF配置(如令牌存储、请求头设置) ) .exceptionHandling(ex -> ex .authenticationEntryPoint(new CustomAuthenticationEntryPoint()) ); return http.build(); } }
额外注意事项
如果接口本身支持POST方法,只是测试时忘记携带CSRF令牌,此时返回403是正常的。Postman测试时需:
- 先发送GET请求获取Cookie中的
XSRF-TOKEN值; - 在POST请求的请求头中添加
X-XSRF-TOKEN,值为获取到的令牌内容。
内容的提问来源于stack exchange,提问作者Triarta
相关产品推荐
相关产品推荐

