"明明接口测试工具能调通,前端调用就报CORS错误!"
"跨域配置加了一堆注解,为什么生产环境突然失效?"
"用了Spring Security后跨域配置全崩了?"
作为全栈开发的必经之痛,跨域问题看似简单实则暗藏杀机。本文将用6种段位解决方案 + 3个真实翻车案例,带你彻底终结Spring Boot跨域难题!
一、跨域本质三句话
- 浏览器安全策略:同源策略(Same-Origin Policy)的主动防御
- 触发条件:协议/域名/端口任一不同即跨域
- 解决方案核心:服务端返回正确的CORS响应头
二、青铜到王者:6种解决方案段位进阶
青铜段位:@CrossOrigin注解(适合新手尝鲜)
java
@RestController
@CrossOrigin(origins = "http://localhost:8080",
allowedHeaders = "*",
methods = {RequestMethod.GET, RequestMethod.POST})
public class UserController {
// 仅对当前Controller生效
}
致命缺陷:
- 每个Controller都要重复配置
- 不支持携带Cookie(credentials问题)
- 无法处理OPTIONS预检请求的缓存
白银段位:全局CORS配置(推荐常规使用)
java
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*") // 生产环境建议指定具体域名
.allowedMethods("*")
.allowedHeaders("*")
.exposedHeaders("Authorization") // 暴露自定义Header
.allowCredentials(true) // 允许携带Cookie
.maxAge(3600); // 预检请求缓存时间
}
}
避坑指南:
- allowCredentials(true)时,allowedOrigins不能为*,必须指定具体域名
- 若同时使用Spring Security,需配合@EnableWebSecurity配置(下文详解)
黄金段位:自定义过滤器(应对特殊场景)
java
@Component
public class CorsFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "http://your-frontend.com");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
response.setHeader("Access-Control-Allow-Credentials", "true");
if ("OPTIONS".equalsIgnoreCase(((HttpServletRequest) req).getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
chain.doFilter(req, res);
}
}
}
适用场景:
- 需要动态控制跨域策略(如多租户系统)
- 处理特殊Header要求(如自定义签名头)
铂金段位:整合Spring Security(高危操作区)
java
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors() // 启用Spring Security的CORS支持
.and()
// 必须禁用CSRF否则会与CORS冲突
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated();
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://trusted-domain.com"));
configuration.setAllowedMethods(Arrays.asList("*"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
血泪教训:
- 同时存在WebMvcConfigurer和Security配置时,以Security配置为准
- 必须显式调用.cors()并配置CorsConfigurationSource
- 使用OAuth2等鉴权方案时,要注意Filter顺序
钻石段位:网关层统一处理(微服务架构推荐)
yaml
# 以Spring Cloud Gateway为例
spring:
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]':
allowed-origins: "https://*.your-domain.com"
allowed-methods: "*"
allowed-headers: "*"
allow-credentials: true
max-age: 1800
架构优势:
- 统一管控所有微服务的跨域策略
- 避免每个服务重复配置
- 支持动态路由更新
王者段位:Nginx反向代理(终极解决方案)
nginx
server {
listen 80;
server_name api.your-domain.com;
location / {
proxy_pass http://spring-boot-app:8080;
# 关键CORS配置
add_header 'Access-Control-Allow-Origin' $http_origin always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
if ($request_method = 'OPTIONS') {
return 204;
}
}
}
降维打击优势:
- 完全解耦业务代码
- 支持复杂跨域策略(如多域名动态匹配)
- 性能损耗最低
三、三大经典翻车现场复盘
翻车1:配置了allowCredentials却报错"Credentials not supported"
原因:
- 响应头Access-Control-Allow-Origin设置为*
- 浏览器安全策略禁止credentials与通配符共存
解决方案:
- 明确指定allowedOrigins为具体域名列表
- 前端axios等库需要设置withCredentials: true
翻车2:POST请求变成OPTIONS请求后404
原因:
- 未正确处理预检请求(OPTIONS方法)
- Spring Security等组件拦截了OPTIONS请求
解决方案:
- 确保服务端处理OPTIONS请求(全局配置或过滤器)
- 在Security配置中放行OPTIONS请求:
- java
- 复制
- 下载
- .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
翻车3:本地开发正常,上线后跨域失效
根本原因:
- 生产环境Nginx未正确传递Origin头
- 网关层缓存了旧的CORS配置
排查步骤:
- 检查浏览器Network面板实际响应头
- 使用curl命令测试:
bash
curl -H "Origin: http://your-domain.com" -I https://api.your-domain.com
- 确认Nginx配置中的add_header指令位置正确
四、最佳实践清单
- 开发环境:使用全局CORS配置+allowedOrigins("*")快速验证
- 生产环境:
- 网关/Nginx层精确控制允许的域名
- 禁用allowedOrigins("*")和allowedHeaders("*")
- 安全加固:
- 限制exposedHeaders仅暴露必要Header
- 对Access-Control-Max-Age设置合理缓存时间
- 鉴权体系:
- 携带Cookie时确保SameSite属性配置
- JWT等方案要处理Authorization头暴露
五、终极验证大法
在Chrome开发者工具中检查响应头是否包含:
http
Access-Control-Allow-Origin: [正确域名]
Access-Control-Allow-Credentials: true # 如需携带凭证
Vary: Origin # 防止CDN缓存错误策略
结语
跨域不是bug,而是浏览器守护你的安全防线。点赞收藏本文,下次遇到CORS问题时,对照这个“生存手册”逐级排查,从此告别熬夜抓狂!
讨论区开放:你遇过最奇葩的跨域问题是什么? 欢迎留言,点