跨域
解决跨域问题有四种方案:jsonp(需要前后端协作,不安全),打包工具(纯前端,只能在开发阶段解决跨域),设置相应头(纯后端),nginx
1. 什么是跨域?
跨域本质上是浏览器为了安全而引入的一种 同源策略 (Same-Origin Policy)。
当一个请求 URL 的 协议 (Protocol)、域名 (Domain)、端口 (Port) 三者中,只要有一个与当前页面的 URL 不同,就会产生跨域。
1.1 同源策略判断表
假设当前页面地址为: http://www.example.com/index.html
| 请求地址 | 结果 | 原因 |
|---|---|---|
http://www.example.com/api/data | 成功 | 同源 |
https://www.example.com/api/data | 跨域 | 协议不同 (http vs https) |
http://api.example.com/data | 跨域 | 域名不同 (www vs api) |
http://www.example.com:8080/data | 跨域 | 端口不同 (80 vs 8080) |
http://www.google.com/api | 跨域 | 域名完全不同 |
1.2 为什么要有同源策略?
主要是为了安全。如果没有同源策略,恶意网站可能会窃取你在其他网站(如银行)的敏感数据(Cookies、LocalStorage),或者利用你的登录状态伪造请求(CSRF 攻击)。
2. 常见的解决方案
解决跨域的核心思路通常有两种:
- 后端配合:允许特定的源访问(CORS)。
- 绕过浏览器:通过服务器代理转发请求(因为服务器之间没有同源策略)。
以下是 5 种最常用的解决方案:
方案一:CORS (Cross-Origin Resource Sharing) 【最推荐】
这是目前最标准、最通用的解决方案。它需要后端在响应头中添加特定的字段,告诉浏览器:“我允许这个源访问我”。
核心响应头:
Access-Control-Allow-Origin: 允许访问的源(例如http://localhost:3000或*)。Access-Control-Allow-Methods: 允许的请求方法(GET, POST, PUT, DELETE 等)。Access-Control-Allow-Headers: 允许的自定义 Header。
代码示例 (Node.js/Express):
const express = require('express');
const app = express();
// 方法 1: 手动设置 Header
app.use((req, res, next) => {
// 允许所有来源访问,生产环境建议指定具体域名
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
next();
});
// 方法 2: 使用 cors 中间件 (推荐)
const cors = require('cors');
app.use(cors());
app.get('/api/data', (req, res) => {
res.json({ msg: '这是跨域数据' });
});
app.listen(3000);
方案二:开发环境代理 (Proxy) 【前端开发常用】
在前端开发阶段(使用 Vue, React, Webpack, Vite 等),由于本地服务器(如 localhost:3000)和后端接口(如 https://www.google.com/search?q=api.server.com)不同源,常使用本地代理解决。
原理: 浏览器 -> 本地开发服务器 (同源) -> 转发请求到后端服务器。 服务器与服务器之间通信不受同源策略限制。
代码示例 (Vite vite.config.js):
export default defineConfig({
server: {
proxy: {
// 当请求路径以 /api 开头时
'/api': {
target: '[http://backend-api.com](http://backend-api.com)', // 目标后端地址
changeOrigin: true, // 修改请求头的 Host 为目标地址
rewrite: (path) => path.replace(/^\/api/, '') // 去掉 /api 前缀
}
}
}
})
注意:这只在开发环境有效,生产环境通常需要 Nginx 配合。
方案三:Nginx 反向代理 【生产环境常用】
在项目上线时,通常使用 Nginx 作为反向代理服务器。前端请求 Nginx,Nginx 再转发给后端,从而实现“同源”的效果。
Nginx 配置示例 (nginx.conf):
server {
listen 80;
server_name [www.example.com](https://www.example.com);
# 前端静态资源
location / {
root /usr/share/nginx/html;
index index.html;
}
# 接口代理
location /api/ {
proxy_pass http://backend-server:8080/; # 转发到真实后端
# 也可以在这里配置 CORS 头
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
}
}
方案四:JSONP (JSON with Padding) 【老旧方案】
JSONP 是利用 <script> 标签不受同源策略限制的特性来实现跨域的。
- 缺点:只支持 GET 请求,不安全(容易受 XSS 攻击),现在已经很少使用,主要用于兼容极老的系统。
前端实现原理:
<script>
function handleResponse(data) {
console.log('获取到的数据:', data);
}
</script>
<!-- 请求后端,并告诉后端回调函数名是 handleResponse -->
<script src="[http://api.example.com/data?callback=handleResponse](http://api.example.com/data?callback=handleResponse)"></script>
方案五:Websocket
Websocket 协议本身不受同源策略限制。如果你使用 Socket.io 等库进行通信,天然支持跨域。
3. 总结与建议
| 场景 | 推荐方案 | 备注 |
|---|---|---|
| 日常开发调试 | Proxy (代理) | 配置简单,无侵入,仅对开发环境有效 |
| 生产环境 API | CORS | 标准做法,后端控制权限,支持所有 HTTP 方法 |
| 项目部署 | Nginx 反向代理 | 性能高,架构清晰,隐藏真实后端地址 |
| 老旧系统兼容 | JSONP | 仅限 GET 请求,不推荐新项目使用 |
最佳实践流程:
- 开发时:前端配置
proxy解决本地开发跨域。 - 上线时:后端配置
CORS允许特定的线上域名访问,或者运维配置Nginx反向代理将前端和后端聚合在同一个域下。