Spring Boot 2023与React自定义Header的CORS问题排查
错误原因分析
"允许源设为*时不能启用凭证"错误
浏览器的CORS安全规则明确要求:当请求携带credentials(Cookie、HTTP认证信息或客户端SSL证书)时,Access-Control-Allow-Origin响应头不能设置为通配符*,必须返回与请求的Origin头完全匹配的具体域名,否则浏览器会直接拦截响应。预检请求无Access-Control-Allow-Origin头错误
你的请求包含自定义CustomAuth头且携带凭证,属于复杂请求,浏览器会先发送OPTIONS预检请求验证服务器是否允许该请求。如果服务器未正确处理OPTIONS请求,未返回Access-Control-Allow-Origin等必要CORS响应头,浏览器就会拦截后续的实际业务请求。
解决方案
1. Spring Boot 全局CORS配置(动态适配任意请求源)
通过全局配置动态获取请求的Origin头作为允许的源,同时开启凭证支持,允许自定义头并正确处理预检请求:
方式一:使用Spring官方CorsFilter(Spring Boot 2.4+推荐)
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; @Configuration public class CorsConfig { @Bean public CorsFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); // 开启凭证支持 config.setAllowCredentials(true); // 允许任意合法请求源(用originPattern适配动态源,支持带凭证的请求) config.addAllowedOriginPattern("*"); // 允许所有HTTP请求方法 config.addAllowedMethod("*"); // 允许自定义头CustomAuth及常用请求头 config.addAllowedHeader("CustomAuth"); config.addAllowedHeader("Content-Type"); config.addAllowedHeader("Accept"); // 设置预检请求缓存时长(秒),避免重复发送OPTIONS请求 config.setMaxAge(3600L); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); // 对所有接口生效 source.registerCorsConfiguration("/**", config); return new CorsFilter(source); } }
方式二:自定义Filter适配旧版本Spring Boot
如果使用Spring Boot 2.4之前的版本,可通过自定义Filter动态获取请求Origin并设置响应头:
import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @Configuration public class CorsFilter implements Filter { @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletResponse response = (HttpServletResponse) res; HttpServletRequest request = (HttpServletRequest) req; String origin = request.getHeader("Origin"); // 动态设置允许的源为请求的Origin response.setHeader("Access-Control-Allow-Origin", origin); response.setHeader("Access-Control-Allow-Credentials", "true"); response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE"); response.setHeader("Access-Control-Max-Age", "3600"); response.setHeader("Access-Control-Allow-Headers", "CustomAuth, Content-Type, Accept"); // 直接响应预检请求,无需转发到业务接口 if ("OPTIONS".equalsIgnoreCase(request.getMethod())) { response.setStatus(HttpServletResponse.SC_OK); return; } chain.doFilter(req, res); } }
2. 控制器接口接收自定义头
在/months接口中通过@RequestHeader获取CustomAuth头:
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RestController; @RestController public class MonthController { @GetMapping("/months") public Object getMonths(@RequestHeader("CustomAuth") String customAuth) { // 此处编写业务逻辑 return yourMonthData; } }
3. React客户端fetch请求配置
确保请求携带凭证并正确设置自定义头:
// 登录请求示例 fetch('/login', { method: 'POST', credentials: 'include', // 携带Cookie等凭证 headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(loginFormData) }) // 获取月份请求示例 fetch('/months', { method: 'GET', credentials: 'include', headers: { 'CustomAuth': yourAuthToken // 携带自定义认证头 } })
关键注意事项
- 不要同时使用全局CORS配置和控制器上的
@CrossOrigin注解,避免配置冲突。 - Android端不受浏览器CORS规则限制,上述配置也能正常支持Android的HTTP请求。
内容的提问来源于stack exchange,提问作者Clown
相关产品推荐
相关产品推荐

