🔹 背景:什么是跨域?
浏览器出于安全性考虑,禁止不同源之间的请求访问资源。当前端尝试请求非同源接口时,就会触发 跨域请求(CORS),如果后端未正确设置 CORS 响应头,就会被浏览器拦截。
同源:
- 协议(http:https)
- 域名
- 端口号
🔸 实现跨域的常见方式(后端)
-
使用 Spring 提供的
CorsFilter
(推荐用于大型项目) -
自定义 Servlet 过滤器
HttpFilter
实现跨域(推荐用于简单项目)
✅ 第一种实现方式:基于 Spring 的 CorsFilter
1. 📦 实现方式
@Configuration
@EnableConfigurationProperties(CorsProperties.class)
@RequiredArgsConstructor
public class CorsConfig {private final CorsProperties corsProperties;@Beanpublic CorsFilter corsFilter() {CorsConfiguration config = new CorsConfiguration();// 允许哪些源访问for (String origin : corsProperties.allowOrigins()) {config.addAllowedOrigin(origin);}// 允许所有请求头config.addAllowedHeader("*");// 允许的 HTTP 方法config.addAllowedMethod("*");// 是否允许发送 Cookieconfig.setAllowCredentials(true);// 拦截路径配置UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();source.registerCorsConfiguration("/**", config);return new CorsFilter(source);}
}
2. 📄 配置文件(application.yml)
novel:cors:allow-origins:- http://localhost:8080- https://example.com
需要配套配置类读取:
@ConfigurationProperties(prefix = "novel.cors")
@Data
public class CorsProperties {private List<String> allowOrigins = new ArrayList<>();
}
✅ 优点
-
灵活性高:允许的来源、方法、请求头、是否允许携带 Cookie 全部可配置。
-
动态配置支持:可通过配置文件实现无需改代码的动态调整。
-
集成度高:完全使用 Spring 框架原生支持,符合标准设计。
-
统一处理路径:通过
/
通配符可拦截所有路径。
⚠️ 缺点
-
依赖 Spring:非 Spring 项目无法使用。
-
配置理解成本较高:
CorsConfiguration
的配置较多,需要熟悉 Spring 的 CORS 概念。 -
性能略低:相比直接操作响应头,封装多了一层抽象(但基本可忽略)。
🔍 实际适用场景
-
后端使用 Spring Boot 框架;
-
项目部署在多个环境,跨域规则需要灵活调整;
-
接口安全性要求高,需要控制来源、方法等。
✅ 第二种实现方式:自定义 Servlet 过滤器 HttpFilter
1. 📦 实现方式
@Component
@Order(Const.ORDER_CORS) // 控制执行顺序,确保最早执行
public class CorsFilter extends HttpFilter {@Overrideprotected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)throws IOException, ServletException {addCorsHeader(request, response);chain.doFilter(request, response);}private void addCorsHeader(HttpServletRequest request, HttpServletResponse response) {String origin = request.getHeader("Origin");// 简单处理:允许所有来源(可改成白名单校验)response.setHeader("Access-Control-Allow-Origin", origin);response.setHeader("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS");response.setHeader("Access-Control-Allow-Headers", "Authorization,Content-Type,X-Client-Type,x-requested-with");response.setHeader("Access-Control-Allow-Credentials", "true");}
}
✅ 优点
-
实现简单直接:手动设置响应头即可,不依赖任何 Spring CORS API。
-
更高可控性:对响应头设置完全手动控制,适合特殊场景。
-
适用范围广:可移植到非 Spring 项目(如 Tomcat、Servlet 原生项目)。
⚠️ 缺点
-
跨域规则硬编码:规则写死在代码中,修改后需重新部署。
-
功能有限:不支持路径级配置、动态来源、配置分组等复杂策略。
-
维护性差:规则增加后代码膨胀,不易管理。
🔍 实际适用场景
-
小型项目或原型开发;
-
跨域规则固定,变动小;
-
后端为纯 Servlet 或不想引入复杂配置。
🧾 对比总结表
特性 | Spring CorsFilter 配置类 | 手写 HttpFilter 实现 |
---|---|---|
是否依赖 Spring | ✅ 是 | ❌ 否 |
灵活性 | ✅ 高(可配置) | ❌ 低(写死) |
是否支持动态配置 | ✅ 支持 | ❌ 不支持 |
是否支持复杂规则 | ✅ 支持路径、方法、头等组合规则 | ❌ 基础 header 设置 |
是否能移植到非 Spring 项目 | ❌ 不可 | ✅ 可 |
实现难度 | 中(需要配置类、Bean 等) | 简单(直接操作响应头) |
推荐场景 | 中大型项目、线上服务 | 快速开发、原型、小工具项目 |
🧠 选择建议总结
项目类型 | 推荐方式 | 原因说明 |
---|---|---|
Spring Boot 项目 | ✅ 使用 Spring CorsFilter | 原生支持,易配置,功能强大 |
跨域规则经常变更 | ✅ 使用 Spring CorsFilter | 支持配置文件修改,无需重启 |
非 Spring 项目 | ✅ 使用手动 HttpFilter | 无依赖,轻量,可独立运行 |
仅允许单一前端域名 | ✅ 任意都可 | 规则简单,两种方式都可行 |
有多环境(dev/test/prod) | ✅ 使用 Spring 配置方案 | 配置灵活,可使用 yml / profile 隔离设置 |
📌 附加建议
-
✅ 预检请求处理(OPTIONS)
如果你使用Spring Security
,确保配置中允许OPTIONS
请求,否则浏览器会因预检失败而报错。 -
✅ Cookie 跨域时需要配置
allowCredentials
与具体Origin
config.setAllowCredentials(true); // 启用后,Origin 不能是 "*",必须是具体域名 config.addAllowedOrigin("http://localhost:8080"); // 指定前端地址
-
⚠️ 不要设置
"*"
同时启用credentials
,这会被浏览器拒绝。
✅ 什么是预检请求(OPTIONS 请求)?
在浏览器发起跨域请求时,如果请求比较“敏感”(如使用了自定义 Header、携带了 Cookie、或者是 PUT/DELETE 方法),浏览器会先自动发送一个 OPTIONS
请求,这就叫做 预检请求(Preflight Request)。
其目的是:
✔️ 浏览器在正式发送请求前,先询问后端:“这个跨域请求你是否接受?”
🧪 示例:
前端发送一个跨域的 POST
请求(带有 Authorization 头):
OPTIONS /api/user HTTP/1.1
Origin: http://localhost:3000
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Authorization
如果后端允许,就返回类似:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: Authorization
然后浏览器才会继续发起真正的 POST 请求。
❗问题来了:Spring Security 默认会拦截 OPTIONS 请求
⚠️ 默认情况下,Spring Security 会拦截所有请求路径(包括 OPTIONS),但不会自动放行预检请求!
结果是:
-
浏览器发出 OPTIONS 请求;
-
后端没处理,Spring Security 拦截了,返回 403 / 无响应;
-
浏览器报错:CORS preflight request failed(预检请求失败);
-
前端永远收不到真正的响应,哪怕你的接口逻辑是对的。
✅ 解决方法:Spring Security 中放行 OPTIONS 请求
你只需要在 WebSecurityConfigurer
中添加如下配置:
@Override
protected void configure(HttpSecurity http) throws Exception {http.cors() // 开启跨域支持.and().csrf().disable().authorizeRequests().antMatchers(HttpMethod.OPTIONS, "/**").permitAll() // ✅ 放行所有 OPTIONS 请求.anyRequest().authenticated();
}
解释:
-
HttpMethod.OPTIONS
:表示放行所有预检请求; -
permitAll()
:告诉 Spring Security,不要拦截这些请求。