Spring Boot 3中PUT请求出现CORS跨域错误求助
问题描述
使用Spring Boot 3.1.6、Spring Security 6.1.5与JDK 17搭建后端服务,已配置CORSConfig类和SecurityConfig中的SecurityFilterChain Bean。通过Postman和服务器端Swagger测试API均正常,但集成React前端后,GET、POST请求可正常调用,PUT请求却出现「No 'Access-Control-Allow-Origin' header is present on the requested resource」错误。
现有配置代码
CORSConfig类
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration @EnableWebMvc public class CORSConfig{ @Bean public WebMvcConfigurer corsConfigurer() { return new WebMvcConfigurer() { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*") .allowedMethods("HEAD", "GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS") .allowedHeaders("*") .allowCredentials(false) .maxAge(3600); } }; } }
SecurityConfig中的SecurityFilterChain Bean
@Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.csrf(csrf -> csrf.disable()) .cors(httpSecurityCorsConfigurer -> httpSecurityCorsConfigurer.configurationSource(request -> new CorsConfiguration().applyPermitDefaultValues())) .exceptionHandling(exception -> exception.authenticationEntryPoint(unauthorizedHandler)) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(auth -> auth.requestMatchers("/api/auth/**").permitAll() .requestMatchers("/swagger-ui/**", "/v3/api-docs/**").permitAll() .requestMatchers("/api/test/**").permitAll() .anyRequest().authenticated() ); http.authenticationProvider(authenticationProvider()); http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class); return http.build(); }
问题原因
Spring Security的CORS配置优先级高于WebMvc的CORS配置。你在Security中使用new CorsConfiguration().applyPermitDefaultValues()时,这个方法默认仅允许GET、HEAD、POST三种请求方法,不包含PUT。当React前端发起PUT请求时,浏览器会先发送OPTIONS预检请求,此时Security返回的响应没有包含允许PUT的CORS头,导致前端报错。
解决方案
可以通过以下两种方式修复:
方案一:统一使用Security的CORS配置(推荐)
移除CORSConfig类,直接在Security配置中指定完整的CORS规则,确保包含PUT方法:
import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import java.util.Arrays; import java.util.Collections; @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { // 定义CORS配置 CorsConfiguration corsConfig = new CorsConfiguration(); corsConfig.setAllowedOriginPatterns(Collections.singletonList("*")); // Spring Boot 2.4+推荐用此方法替代allowedOrigins corsConfig.setAllowedMethods(Arrays.asList("HEAD", "GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS")); corsConfig.setAllowedHeaders(Collections.singletonList("*")); corsConfig.setAllowCredentials(false); corsConfig.setMaxAge(3600L); // 配置CORS源 UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", corsConfig); http.csrf(csrf -> csrf.disable()) .cors(cors -> cors.configurationSource(source)) // 使用自定义CORS配置 .exceptionHandling(exception -> exception.authenticationEntryPoint(unauthorizedHandler)) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(auth -> auth.requestMatchers("/api/auth/**").permitAll() .requestMatchers("/swagger-ui/**", "/v3/api-docs/**").permitAll() .requestMatchers("/api/test/**").permitAll() .anyRequest().authenticated() ); http.authenticationProvider(authenticationProvider()); http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class); return http.build(); }
方案二:让Security复用WebMvc的CORS配置
保留CORSConfig类,修改Security中的CORS配置,使其复用WebMvc的CORS规则:
import org.springframework.web.servlet.config.annotation.CorsRegistry; @Bean public SecurityFilterChain filterChain(HttpSecurity http, WebMvcConfigurer webMvcConfigurer) throws Exception { http.csrf(csrf -> csrf.disable()) .cors(cors -> cors.configurationSource(request -> { // 从WebMvcConfigurer中提取已配置的CORS规则 CorsRegistry registry = new CorsRegistry(); webMvcConfigurer.addCorsMappings(registry); return registry.getCorsConfigurations().get("/**"); })) .exceptionHandling(exception -> exception.authenticationEntryPoint(unauthorizedHandler)) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(auth -> auth.requestMatchers("/api/auth/**").permitAll() .requestMatchers("/swagger-ui/**", "/v3/api-docs/**").permitAll() .requestMatchers("/api/test/**").permitAll() .anyRequest().authenticated() ); http.authenticationProvider(authenticationProvider()); http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class); return http.build(); }
额外注意事项
- 如果需要允许携带凭证(
allowCredentials(true)),则allowedOrigins不能设为*,必须指定具体的前端域名,比如http://localhost:3000 - 确保前端发起PUT请求时没有携带未被允许的额外请求头,当前配置
allowedHeaders("*")已覆盖此场景
内容的提问来源于stack exchange,提问作者Pushpendra Kushvaha
相关产品推荐
相关产品推荐

