1. 为什么需要关注Spring跨域CORS Filter?
在前后端分离架构成为主流的今天,跨域问题就像一道无形的墙,把前端应用和后端服务隔开。我最近接手的一个电商项目就遇到了典型场景:Vue前端运行在localhost:8080,而Spring Boot后端在localhost:8090,当axios发起请求时,浏览器控制台赫然出现那个熟悉的红色错误——"has been blocked by CORS policy"。
跨域问题本质是浏览器的同源策略在作祟。作为安全机制,它要求协议、域名、端口三者完全相同才允许通信。但在微服务架构下,服务拆分、多端接入成为常态,跨域处理就成了每个Spring开发者必须掌握的技能。
2. CORS核心机制解析
2.1 预检请求(Preflight)的运作原理
当请求满足以下任一条件时,浏览器会自动发起OPTIONS预检请求:
- 使用PUT/DELETE等非简单方法
- 自定义请求头(如Authorization)
- Content-Type非
application/x-www-form-urlencoded、multipart/form-data或text/plain
我曾用Postman测试接口一切正常,但浏览器却报错,就是因为忽略了预检机制。后端必须正确处理OPTIONS请求,返回包含这些头的响应:
http复制Access-Control-Allow-Origin: http://localhost:8080
Access-Control-Allow-Methods: GET,POST,PUT
Access-Control-Allow-Headers: Content-Type,Authorization
Access-Control-Max-Age: 3600
2.2 关键响应头深度解读
-
Access-Control-Allow-Origin
实测发现使用*通配符时,携带凭证(credentials)的请求会失败。建议动态设置:java复制@Value("${cors.allowed-origins}") private String[] allowedOrigins; public void setAllowedOrigin(HttpServletResponse response, String origin) { if(Arrays.asList(allowedOrigins).contains(origin)){ response.setHeader("Access-Control-Allow-Origin", origin); } } -
Access-Control-Expose-Headers
默认只暴露6个简单响应头。若需要让前端获取自定义头(如X-Total-Count),必须显式声明:java复制response.addHeader("Access-Control-Expose-Headers", "X-Total-Count");
3. Spring中的三种CORS解决方案
3.1 注解方案:@CrossOrigin的利与弊
在Controller方法或类上添加注解是最快捷的方式:
java复制@CrossOrigin(
origins = "http://localhost:8080",
methods = {RequestMethod.GET, RequestMethod.POST},
allowedHeaders = "*"
)
@GetMapping("/api/products")
public List<Product> listProducts() {...}
但实际项目中我发现两个坑:
- 粒度太细导致重复配置
- 无法处理全局需求(如拦截器添加的自定义头)
3.2 全局配置:WebMvcConfigurer方案
更适合生产环境的配置方式:
java复制@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("http://localhost:8080", "https://prod.com")
.allowedMethods("*")
.allowCredentials(true)
.exposedHeaders("X-Total-Count")
.maxAge(3600);
}
}
重要提示:当同时使用Spring Security时,必须在Security配置中显式启用CORS:
java复制http.cors().configurationSource(request -> new CorsConfiguration().applyPermitDefaultValues());
3.3 自定义Filter的进阶控制
对于需要动态鉴权等复杂场景,手动实现Filter更灵活:
java复制public class CustomCorsFilter 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");
if(isAllowedOrigin(origin)) {
response.setHeader("Access-Control-Allow-Origin", origin);
}
// 预检请求直接返回
if ("OPTIONS".equals(request.getMethod())) {
response.setHeader("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE");
response.setHeader("Access-Control-Allow-Headers", "Content-Type,Authorization");
response.setHeader("Access-Control-Max-Age", "3600");
response.setStatus(HttpServletResponse.SC_OK);
return;
}
chain.doFilter(req, res);
}
}
注册Filter时需注意顺序问题——要在Spring Security过滤器之前:
java复制@Bean
public FilterRegistrationBean<CustomCorsFilter> corsFilter() {
FilterRegistrationBean<CustomCorsFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new CustomCorsFilter());
registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
return registration;
}
4. 生产环境实战经验
4.1 多环境配置管理
在application.yml中区分环境配置:
yaml复制# dev环境
cors:
allowed-origins:
- "http://localhost:8080"
- "http://test.com"
# prod环境
cors:
allowed-origins:
- "https://prod.com"
- "https://admin.prod.com"
4.2 浏览器兼容性处理
Edge和Firefox对CORS的实现有差异:
- Firefox加载本地文件时需设置
security.fileuri.strict_origin_policy为false - Edge旧版本对
Access-Control-Allow-Headers区分大小写
4.3 性能优化技巧
- 合理设置
max-age减少预检请求(但不宜过大,建议2小时) - 对静态资源使用CDN规避跨域
- 启用HTTP/2的服务器推送减少请求次数
5. 常见问题排查指南
5.1 错误:"Credentials not supported with wildcard origin"
当响应头包含:
http复制Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
解决方案:
- 指定具体origin而非
* - 前端axios配置
withCredentials: true
5.2 错误:"Request header field authorization is not allowed"
缺失Access-Control-Allow-Headers配置。建议:
java复制.allowedHeaders("authorization", "content-type", "x-requested-with")
5.3 预检请求未触发的情况
检查是否满足简单请求条件:
- 仅限GET/HEAD/POST方法
- 仅含Accept/Accept-Language/Content-Language/Content-Type
- Content-Type为上述三种之一
6. 安全加固建议
- 严格限制allowedOrigins,避免使用
* - 对敏感接口禁用CORS,走API网关鉴权
- 结合CSRF保护(但注意CORS与CSRF的互补关系)
- 监控异常的OPTIONS请求
我在实际项目中发现,合理的CORS配置应该像门卫——既不能把所有人挡在门外,也不能放任不管。建议采用白名单机制,并通过环境变量动态管理允许的源,这在容器化部署时尤为重要。
