在单体架构时代,跨域问题还不算突出;但进入微服务、前后端分离、多端统一时代,跨域几乎是每个项目必踩的坑。尤其在微服务架构下,网关、认证、分布式部署、多域名并存,让跨域变得更复杂、更隐蔽。
本文从浏览器同源策略讲起,结合微服务真实场景,把跨域原理、常见方案、网关统一处理、避坑要点一次性讲透,适合直接发布 CSDN 学习与收藏。
一、什么是跨域?为什么会出现跨域?
1.1 同源策略(Same-Origin Policy)
跨域限制,本质是浏览器的安全机制,服务器本身不存在跨域限制。
同源 = 协议 + 域名 + 端口 三者完全一致只要有一个不一样,浏览器就判定为 "跨域",会拦截响应,前端拿不到数据。
举例(以 http://a.com:8080 为基准):
表格
| 请求地址 | 是否跨域 | 原因 |
|---|---|---|
http://a.com:8080/api |
否 | 完全同源 |
https://a.com:8080/api |
是 | 协议不同(http/https) |
http://api.a.com:8080 |
是 | 域名不同 |
http://a.com:9090/api |
是 | 端口不同 |
1.2 为什么微服务更容易跨域?
微服务天生就是多应用、多域名、多端口、多实例部署:
- 前端:
http://localhost:8080 - 网关:
http://gateway:8888 - 用户服务:
http://user:8001 - 订单服务:
http://order:8002
只要前端直接 / 间接访问非同源地址,就会触发跨域。
典型报错:
plaintext
Access to XMLHttpRequest at 'xxx' from origin 'xxx' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
二、跨域核心知识点:简单请求 & 预检请求
2.1 简单请求
满足以下全部条件为简单请求:
- 方法:
GET / HEAD / POST - 请求头只包含:
Accept、Accept-Language、Content-Type等 - Content-Type 仅限:
application/x-www-form-urlencodedmultipart/form-datatext/plain
特点:只发一次请求,直接携带跨域头。
2.2 预检请求(OPTIONS)
不满足简单请求,就会触发预检:
- 自定义请求头(如
token) Content-Type: application/json- 使用
PUT / DELETE / PATCH
浏览器会先发一次 OPTIONS 请求,询问服务器:
- 允许哪些来源?
- 允许哪些方法?
- 允许哪些头?
服务器通过后,才发送真实请求。
微服务常见坑:后端只处理了真实请求,没处理 OPTIONS,导致跨域一直失败。
三、微服务架构下跨域的常见解决方案
方案 1:前端代理(开发环境专用)
Vue / React / Angular 都支持 devServer.proxy。
原理:前端请求自己 → 代理转发到后端 → 浏览器认为同源
示例(vue.config.js):
js
devServer: {
proxy: {
'/api': {
target: 'http://localhost:8888',
changeOrigin: true
}
}
}
适用:
- 仅限开发环境
- 生产环境不能用
方案 2:单个服务配置 CORS(不推荐微服务)
在每个微服务中单独配置跨域。
Spring Boot 示例:
java
运行
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET","POST","PUT","DELETE","OPTIONS")
.allowCredentials(true)
.maxAge(3600);
}
}
缺点:
- 每个服务都要写一遍
- 网关、认证、服务之间不一致
- 维护爆炸,不适合微服务
方案 3:网关统一处理跨域(微服务标准方案 ✅)
微服务最佳实践 :网关统一处理跨域,下游微服务关闭跨域配置。
常见网关:
- Spring Cloud Gateway
- GatewayNet / Ocelot(.NET)
- Nginx
- Kong / APISIX
Spring Cloud Gateway 配置跨域示例
yaml
spring:
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]':
allowedOrigins: "*"
allowedMethods: "*"
allowedHeaders: "*"
allowCredentials: true
maxAge: 3600
优点:
- 统一配置,一处修改全局生效
- 避免下游服务重复配置
- 完美适配微服务
方案 4:Nginx 反向代理(生产常用)
前端 → Nginx → 网关 → 微服务
Nginx 配置:
plaintext
location /api/ {
proxy_pass http://gateway:8888/;
add_header Access-Control-Allow-Origin *;
}
适合:
- 对外正式环境
- 多域名、多端统一接入
方案 5:JSONP(淘汰)
只支持 GET,不安全,微服务基本不用。
方案 6:前端与后端同域名部署(最彻底)
前端打包后放到后端静态资源目录,或同域名 Nginx 代理。同源自然无跨域。
四、微服务跨域高频坑点(重点!)
4.1 带 Cookie / Token 时跨域失效
前端开启 withCredentials:
js
axios.defaults.withCredentials = true
后端必须:
Access-Control-Allow-Origin不能为*- 必须配置具体域名
allowCredentials = true
4.2 OPTIONS 预检请求被拦截
- 认证过滤器(JWT/Security)拦截 OPTIONS
- 网关鉴权优先于跨域配置
解决:放行所有 OPTIONS 请求,不做认证
4.3 跨域头重复
网关配置了跨域 + 微服务也配置了跨域导致响应头重复,浏览器报错。
原则:网关处理跨域,下游服务禁止再配置 CORS。
4.4 开放 * 导致线上安全风险
测试可用 *,生产必须指定具体域名。
五、微服务跨域最佳实践总结
- 开发环境:前端代理
- 微服务架构:网关统一处理 CORS
- 生产环境:Nginx 反向代理 + 网关鉴权
- 认证场景:禁止 Origin=*,使用具体域名 + allowCredentials
- OPTIONS 请求:统一放行,不参与鉴权
- 避免重复配置:只在网关一层处理跨域
六、结语
跨域不是技术难题,而是架构规范问题。在微服务里,只要记住一句话:
跨域统一在网关处理,下游服务只负责业务,不关心跨域。
这套方案能适配:Spring Cloud、.NET 微服务、Go 微服务、K8s 集群、Service Mesh 等所有现代架构。