在前后端分离架构中,“跨域” 是开发者绕不开的问题 —— 前端页面在http://localhost:3000请求后端http://localhost:8080的接口时,浏览器会因 “同源策略” 拦截响应,报出Access to fetch at '...' from origin '...' has been blocked by CORS policy错误。跨域问题的本质是浏览器的安全限制,解决它需要在 “安全” 与 “便捷” 之间找到平衡,实现合法的跨域资源共享。
跨域的核心概念与判定标准
什么是同源策略?
浏览器的同源策略要求 “协议、域名、端口” 三者完全一致才视为 “同源”,否则即为跨域。例如:
-
http://example.com与https://example.com:协议不同 → 跨域 -
http://example.com与http://api.example.com:域名不同 → 跨域 -
http://example.com:80与http://example.com:8080:端口不同 → 跨域
同源策略的目的是防止恶意网站窃取其他网站的用户数据(如 Cookie、LocalStorage),但也阻碍了合法的前后端分离架构通信。
跨域请求的分类
- 简单请求:满足 “GET/POST/HEAD 方法 + 仅含简单请求头(如 Content-Type 为 application/x-www-form-urlencoded)” 的请求,直接发送并根据响应头判断是否允许跨域。
- 预检请求(OPTIONS) :复杂请求(如 PUT/DELETE 方法、含自定义头、Content-Type 为 application/json)会先发送 OPTIONS 请求,询问服务器是否允许跨域,只有通过后才发送实际请求。
后端解决跨域的三种方案
1. 全局 CORS 配置:最常用的跨域解决方案
通过 Spring Boot 的@CrossOrigin注解或全局配置类,允许指定来源的跨域请求:
// 全局CORS配置类
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // 对所有接口生效
.allowedOrigins("http://localhost:3000", "https://example.com") // 允许的前端域名
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") // 允许的请求方法
.allowedHeaders("*") // 允许的请求头
.exposedHeaders("Authorization") // 允许前端获取的响应头
.allowCredentials(true) // 允许携带Cookie
.maxAge(3600); // 预检请求的缓存时间(秒),减少OPTIONS请求次数
}
}
关键参数说明:
allowedOrigins:指定允许的域名(生产环境不要用*,避免安全风险)allowCredentials: true:允许跨域请求携带 Cookie(此时allowedOrigins不能为*)maxAge:设置预检请求的缓存时间,减少重复 OPTIONS 请求的开销
2. 网关层跨域:适合微服务架构
在 API 网关(如 Spring Cloud Gateway)统一处理跨域,避免在每个微服务中重复配置:
# Spring Cloud Gateway跨域配置
spring:
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]':
allowed-origins:
- "http://localhost:3000"
- "https://example.com"
allowed-methods: "*"
allowed-headers: "*"
allow-credentials: true
max-age: 3600
优势:
- 集中管理跨域规则,无需在各服务中配置
- 可结合网关的路由规则,为不同路径设置不同跨域策略
3. JSONP:兼容老式浏览器的方案
JSONP 利用<script>标签不受同源策略限制的特性,通过回调函数获取跨域数据(仅支持 GET 请求):
@GetMapping("/jsonp/data")
public String getJsonpData(@RequestParam String callback) {
// 业务逻辑:获取数据
Map<String, Object> data = new HashMap<>();
data.put("id", 1);
data.put("name", "示例数据");
// 包装成JSONP格式(callback函数调用)
return callback + "(" + JSON.toJSONString(data) + ")";
}
前端调用:
<script src="http://localhost:8080/jsonp/data?callback=handleData"></script>
<script>
function handleData(data) {
console.log("跨域数据:", data); // 输出后端返回的数据
}
</script>
局限性:
- 仅支持 GET 方法,不适合复杂请求
- 存在 XSS 安全风险(需严格校验回调函数名)
- 现代项目已基本被 CORS 替代
跨域配置的安全风险与规避
1. 过度宽松的配置导致安全漏洞
- 风险:
allowedOrigins: "*"允许所有域名跨域,可能被恶意网站利用窃取数据 - 规避:仅允许信任的域名(如公司前端域名、合作方域名)
2. 携带 Cookie 的跨域风险
- 风险:
allowCredentials: true时,若allowedOrigins为*,浏览器会拒绝响应;即使配置正确,也需防范 CSRF 攻击 - 规避:结合 CSRF Token 验证,确保请求来自合法前端
3. 预检请求过多影响性能
- 风险:复杂请求每次都发送 OPTIONS 预检请求,增加网络开销
- 规避:合理设置
maxAge(如 3600 秒),缓存预检结果;尽量使用简单请求(如 POST+application/x-www-form-urlencoded)
避坑指南
-
前后端分离必配跨域:开发环境先配置好跨域,避免调试时被跨域报错干扰
-
注意 Cookie 的 SameSite 属性:若跨域请求需要携带 Cookie,需确保 Cookie 的
SameSite属性为None(同时需设置Secure属性,仅在 HTTPS 下生效) -
排查跨域问题的步骤:
-
查看浏览器控制台的具体错误信息(是 Origin 不允许还是方法不允许)
-
检查 Response Headers 是否包含
Access-Control-Allow-*相关头 -
复杂请求需确认 OPTIONS 预检请求是否成功(状态码 200)
-
跨域问题虽看似简单,却涉及浏览器安全机制的细节。合理的跨域配置既能让前后端顺畅通信,又能守住安全底线,这是后端开发者在前后端分离架构中必须掌握的 “基础功”。