React调用Spring Boot接口CORS预检请求未通过报错排查
问题场景
- 前端技术栈:React,本地运行地址为
http://localhost:3000 - 后端技术栈:Spring Boot,本地运行地址为
http://localhost:9001 - 业务逻辑:前端向
/api/order接口发起POST请求提交订单数据,携带JWT鉴权头与JSON格式请求体,前端请求代码如下:
await fetch('http://localhost:9001/api/order', { headers: { 'Content-Type': 'application/json' , 'Authorization': 'Bearer ' + token }, method: 'POST', body: JSON.stringify({ 'customerName': username.split("@")[0], 'email': username, 'phoneNumber': '9988776655', 'amount': location.state.price.toString() }) }).then(function (response, err) { console.log(response,' <<<< >>>> ', err) })
报错现象
请求发起后被浏览器CORS策略拦截,控制台报错如下:
源地址'http://localhost:3000'对'http://localhost:9001/api/order'的fetch访问已被CORS策略阻止:预检请求响应未通过访问控制校验,请求的资源上未携带'Access-Control-Allow-Origin'响应头。如果业务可接受不透明响应,可将请求的mode设置为'no-cors',在禁用CORS的情况下获取资源。
排查说明
已在Spring Boot侧完成常规MVC层面的CORS跨域配置,但问题始终复现,最终定位问题出在Spring Security配置链路。
根因分析
Spring Security过滤器链执行优先级高于Spring MVC跨域过滤器,浏览器在发起带自定义头(Authorization)、JSON格式请求体的POST请求前,会先发OPTIONS方法的预检请求,该请求会先被Security过滤器拦截:
- 未在Security配置中开启CORS支持时,常规MVC层配置的跨域规则不会作用到Security过滤器链,预检请求无法返回
Access-Control-Allow-Origin等要求的跨域响应头 - 若Security配置未放行OPTIONS请求,预检请求会直接被鉴权逻辑拦截返回401/403响应,自然不会携带合法跨域头,触发浏览器CORS拦截。
修复方案
修改继承WebSecurityConfigurerAdapter的Spring Security配置类,做两处调整:
- 在HttpSecurity配置中开启cors支持,让Security链路加载已配置的跨域规则
- 放行所有OPTIONS类型的预检请求,不需要走鉴权校验
示例配置代码:
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http // 开启Security链路的CORS支持,自动读取容器中配置的CorsConfigurationSource .cors().and() .csrf().disable() .authorizeRequests() // 放行所有OPTIONS预检请求 .antMatchers(HttpMethod.OPTIONS, "/**").permitAll() // 其余接口鉴权规则按业务需求配置即可 .antMatchers("/api/order").authenticated() .anyRequest().permitAll(); } // 若之前未配置全局跨域规则,可直接在Security配置类中注入跨域规则 @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); configuration.setAllowedOrigins(List.of("http://localhost:3000")); configuration.setAllowedMethods(List.of("GET","POST","PUT","DELETE","OPTIONS")); configuration.setAllowedHeaders(List.of("*")); configuration.setAllowCredentials(true); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); return source; } }
内容的提问来源于stack exchange,提问作者Shobhit Ranjan
相关产品推荐
相关产品推荐

