SpringBoot实战:三种主流CORS跨域配置方案详解与选型
1. 为什么我们需要CORS跨域解决方案
第一次遇到CORS报错的时候,我正对接一个前后端分离项目。前端同事信誓旦旦说接口调不通,我这边日志明明显示请求已经处理成功了。打开浏览器控制台,那个经典的红色报错赫然在目:"Access to XMLHttpRequest at 'http://api.example.com' from origin 'http://localhost:3000' has been blocked by CORS policy..." 这才意识到遇到了传说中的跨域问题。
同源策略就像小区的门禁系统。假设你住在A小区(前端域名),想去B小区(后端接口)拜访朋友。如果两个小区同属一个物业(同源),门卫直接放行;如果是不同物业(跨域),门卫就会盘查访问权限。CORS就是B小区物业给你开的访客通行证,告诉门卫:"这位访客我认识,放行吧!"
现代Web开发中,跨域场景无处不在:
- 开发阶段:前端localhost:3000调用后端localhost:8080
- 生产环境:前端www.example.com调用api.example.com
- 微服务架构:网关gateway.example.com路由到各业务服务
2. 注解方案:@CrossOrigin快速上手
2.1 单方法级配置
刚接触SpringBoot时,我最喜欢用@CrossOrigin注解。就像给控制器方法贴便利贴一样简单:
@RestController @RequestMapping("/api") public class UserController { @CrossOrigin @GetMapping("/users") public List<User> listUsers() { // 返回用户列表 } }这个注解默认允许所有来源(origins = "*"),支持GET、HEAD、POST方法。实测在SpringBoot 2.5+环境下,只需要这行代码就能让前端顺利拿到数据。
2.2 类级别与全局配置
随着项目扩大,我给每个方法都加注解实在太麻烦。这时候可以升级用法:
@CrossOrigin(origins = "https://myfrontend.com", maxAge = 3600, allowedHeaders = "*") @RestController @RequestMapping("/api") public class ProductController { // 所有方法都继承跨域配置 }更聪明的做法是创建一个基础控制器:
@CrossOrigin public class BaseController {} @RestController @RequestMapping("/order") public class OrderController extends BaseController { // 自动获得跨域支持 }注意:继承方式在Spring 5.3之后更推荐用
@ControllerAdvice实现,避免类继承的强耦合。
3. 全局配置:WebMvcConfigurer方案
3.1 基础配置模板
当项目有几十个控制器时,我转向了全局配置方案。新建一个配置类:
@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/**") .allowedOrigins("https://production.com", "http://localhost:3000") .allowedMethods("GET", "POST", "PUT") .allowCredentials(true) .maxAge(1800); } }这个配置实现了:
- 只对/api开头的接口启用CORS
- 允许生产环境和本地开发环境跨域访问
- 开放三种HTTP方法
- 支持携带Cookie(前端需要配合withCredentials)
- 预检请求缓存30分钟
3.2 多环境差异化配置
在实际项目中,我常用Spring Profile实现环境隔离:
@Profile("dev") @Configuration public class DevCorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**").allowedOrigins("*"); } } @Profile("prod") @Configuration public class ProdCorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/**") .allowedOrigins("https://official.website"); } }开发环境放开所有跨域限制,生产环境则严格限定可信域名。通过spring.profiles.active=dev切换配置。
4. 过滤器方案:最灵活的CORS控制
4.1 基础过滤器实现
当项目需要与老旧系统集成时,Filter方案往往最能打。这是我常用的模板:
@Component public class CustomCorsFilter 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", "*"); response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE"); response.setHeader("Access-Control-Max-Age", "3600"); response.setHeader("Access-Control-Allow-Headers", "x-requested-with, authorization, content-type"); if ("OPTIONS".equalsIgnoreCase(((HttpServletRequest) req).getMethod())) { response.setStatus(HttpServletResponse.SC_OK); } else { chain.doFilter(req, res); } } }这个实现特点:
- 处理OPTIONS预检请求直接返回200
- 允许所有来源的简单请求(生产环境应替换为具体域名)
- 设置1小时预检缓存
- 支持常见请求头和四种HTTP方法
4.2 动态域名白名单
在SAAS系统中,我遇到过需要动态允许租户域名的需求。改良后的过滤器:
public class DynamicCorsFilter implements Filter { @Autowired private TenantConfigService configService; @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; String origin = request.getHeader("Origin"); if (configService.isAllowedOrigin(origin)) { response.setHeader("Access-Control-Allow-Origin", origin); response.setHeader("Access-Control-Allow-Credentials", "true"); } // 其他配置... } }通过查询数据库或Redis中的租户配置,实现动态域名校验。这种方案在多租户系统中特别实用。
5. 方案对比与选型指南
5.1 特性对比表
| 特性 | @CrossOrigin | WebMvcConfigurer | Filter |
|---|---|---|---|
| 配置粒度 | 方法/类级别 | 全局/路径级别 | 全局 |
| 动态域名支持 | ✔ | ||
| 支持Spring版本 | 4.2+ | 所有版本 | 所有版本 |
| 处理OPTIONS请求 | 自动 | 自动 | 需手动处理 |
| 与Spring Security兼容性 | 中等 | 良好 | 最佳 |
5.2 选型建议
根据我踩坑经验,推荐这些场景选择:
选择@CrossOrigin当:
- 快速原型开发
- 只有少量接口需要跨域
- 项目使用最新Spring版本
选择WebMvcConfigurer当:
- 需要统一管理跨域规则
- 项目已有WebMvc配置类
- 需要路径模式匹配
选择Filter当:
- 需要动态域名控制
- 项目使用老旧Spring版本
- 与Spring Security深度集成
- 需要处理特殊Header逻辑
在微服务网关层,我通常采用Filter方案,因为需要处理复杂的跨域场景。而单体应用中使用WebMvcConfigurer更简洁。
