深度解析跨域问题:真实场景、解决方案与进阶方案
深度解析跨域问题:真实场景、解决方案与进阶方案
跨域,是每个前端开发者都绕不开的"拦路虎"。本文将从实际开发场景出发,详细剖析跨域问题的成因、解决思路以及多种备选方案,并用流程图帮你理清请求流程。
一、什么是跨域?为什么会出现?
跨域(Cross-Origin)指的是浏览器同源策略 (Same-Origin Policy)的限制。同源策略要求:协议、域名、端口号 三者完全一致,才能共享资源。只要有一项不同,浏览器就会阻止 XMLHttpRequest、Fetch 等请求的响应结果。
同源策略是浏览器安全的基石,但它也间接"误伤"了正常的跨域通信需求。
常见跨域场景举例
当前页面地址
请求地址
是否跨域
原因
http://www.a.com
http://www.a.com/api
否
同源
http://www.a.com
https://www.a.com/api
是
协议不同(http vs https)
http://www.a.com
http://www.b.com/api
是
域名不同
http://www.a.com:8080
http://www.a.com:80/api
是
端口不同
二、真实场景 ------ 我在项目中遇到的跨域问题
场景一:前后端分离开发环境
背景 :前端使用 Vue + webpack-dev-server 启动在 http://localhost:8080,后端 Spring Boot 启动在 http://localhost:8081。前端调用登录接口时,浏览器报错:
复制代码
Access to XMLHttpRequest at 'http://localhost:8081/login' from origin 'http://localhost:8080' has been blocked by CORS policy
分析:端口号不一致(8080 vs 8081),触发了跨域。这是开发环境中最常见的场景。
场景二:微服务架构中的前端调用
背景 :前端页面部署在 https://gw.xxx.com,需要分别调用订单服务(order.svc.com)和用户服务(user.svc.com)。由于域名不同,所有接口都被跨域策略拦截。
场景三:第三方开放 API 调用
背景 :一个天气查询页面,前端直接通过 axios 请求 http://api.weather.com/data,浏览器报跨域错误。因为第三方 API 没有返回 Access-Control-Allow-Origin 响应头。
三、解决方案 ------ 从根源到实战
方案一:CORS(跨域资源共享)★★★★★
原理:服务器通过添加特定的响应头,明确告诉浏览器"允许某个源访问"。这是目前最标准、最彻底的解决方案,支持所有 HTTP 方法(GET、POST、PUT、DELETE 等)。
实现:在后端代码中配置 CORS 过滤器或中间件。
Spring Boot 示例(全局配置)
java
复制代码
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // 允许所有接口
.allowedOrigins("http://localhost:8080") // 允许的前端源
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowCredentials(true)
.maxAge(3600);
}
}
Node.js (Express) 示例
javascript
复制代码
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', 'http://localhost:8080');
res.header('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
});
Nginx 反向代理中配置 CORS
nginx
复制代码
location /api/ {
add_header Access-Control-Allow-Origin $http_origin;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,Keep-Alive,Content-Type';
if ($request_method = 'OPTIONS') {
return 204;
}
}
方案二:代理服务器(devServer / Nginx)★★★★☆
原理:同源策略只针对浏览器,服务器之间没有跨域限制。将前端请求先发送到与前端同源的代理服务,由代理转发到真正的后端,再把响应返回给浏览器。
适用场景:开发环境快速解决、不想修改后端代码、或者需要对多个后端进行聚合。
Vue CLI 中配置 devServer 代理
javascript
复制代码
// vue.config.js
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://localhost:8081', // 后端地址
changeOrigin: true, // 修改请求头中的host
pathRewrite: { '^/api': '' } // 重写路径
}
}
}
}
Nginx 反向代理配置
nginx
复制代码
server {
listen 80;
server_name myapp.com;
location /api/ {
proxy_pass http://backend-service:8081/;
proxy_set_header Host $host;
}
}
方案三:JSONP(仅限 GET 请求)★★☆☆☆
原理 :利用