解决跨域问题的6种方案
解决跨域问题(Cross-Origin Resource Sharing, CORS)是 Web 开发中常见的需求,以下是 6 种主流解决方案,涵盖前端、后端和服务器配置等不同层面:
一、CORS(跨域资源共享)
原理
通过服务器设置响应头 Access-Control-Allow-Origin
,允许指定域访问资源。
实现
后端代码示例(Node.js/Express):
app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "*"); // 允许所有域
res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
res.header("Access-Control-Allow-Headers", "Content-Type, Authorization");
next();
});
适用场景
- 标准化方案,支持所有 HTTP 方法(GET/POST/PUT/DELETE 等)。
- 需要后端配合,适合前后端分离的生产环境。
二、代理服务器(Proxy)
原理
通过同源服务器转发请求,绕过浏览器跨域限制。
实现
前端开发环境(Vue/React):
// vue.config.js
module.exports = {
devServer: {
proxy: {
"/api": {
target: "http://target-server.com",
changeOrigin: true,
pathRewrite: { "^/api": "" }
}
}
}
};
生产环境(Nginx 反向代理):
server {
listen 80;
server_name frontend.com;
location /api/ {
proxy_pass http://backend.com/;
proxy_set_header Host $host;
}
}
适用场景
- 开发环境常用,生产环境通过 Nginx 配置。
- 无需修改前端代码,适合无法修改后端的场景。
三、JSONP(JSON with Padding)
原理
利用 <script>
标签不受跨域限制的特性,通过回调函数传递数据。
实现
前端代码:
function handleResponse(data) {
console.log("Received data:", data);
}
const script = document.createElement("script");
script.src = "http://api.com/data?callback=handleResponse";
document.body.appendChild(script);
后端代码(Node.js):
app.get("/data", (req, res) => {
const callback = req.query.callback;
const data = { message: "Hello JSONP" };
res.send(`${callback}(${JSON.stringify(data)})`);
});
适用场景
- 仅支持 GET 请求。
- 适用于老旧浏览器或无需敏感数据交互的场景。
四、WebSocket
原理
WebSocket 协议不受同源策略限制,可双向通信。
实现
前端代码:
const socket = new WebSocket("ws://api.com/socket");
socket.onmessage = (event) => {
console.log("Received:", event.data);
};
后端代码(Node.js/ws 库):
const WebSocket = require("ws");
const server = new WebSocket.Server({ port: 8080 });
server.on("connection", (socket) => {
socket.send("Connected!");
});
适用场景
- 实时通信(如聊天、股票行情)。
- 需要全双工通信的场景。
五、PostMessage + iframe
原理
通过 postMessage
API 实现跨域窗口通信。
实现
父页面(parent.com):
const iframe = document.getElementById("child-iframe");
iframe.contentWindow.postMessage("Hello from parent", "http://child.com");
子页面(child.com):
window.addEventListener("message", (event) => {
if (event.origin === "http://parent.com") {
console.log("Received:", event.data);
}
});
适用场景
- 跨域页面间通信(如嵌入第三方组件)。
- 需要安全验证
event.origin
。
六、Nginx 反向代理
原理
通过 Nginx 配置将请求转发到目标服务器,统一解决跨域问题。
实现
server {
listen 80;
server_name frontend.com;
location /api/ {
proxy_pass http://backend.com/;
add_header Access-Control-Allow-Origin *;
}
}
适用场景
- 生产环境推荐方案。
- 统一管理跨域配置,减少后端压力。
方案对比表
方案 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
CORS | 标准化,支持所有 HTTP 方法 | 需后端配合 | 前后端分离的生产环境 |
代理服务器(Proxy) | 无需后端修改,开发便捷 | 仅开发环境或需配置 Nginx | 开发环境/生产环境反向代理 |
JSONP | 兼容性好 | 仅支持 GET | 老旧浏览器或简单数据获取 |
WebSocket | 实时双向通信 | 需额外维护 WebSocket 服务 | 实时聊天、通知推送 |
PostMessage | 安全可控 | 依赖 iframe 嵌套 | 跨域页面间通信 |
Nginx 反向代理 | 统一配置,性能高 | 需运维介入配置 | 生产环境大规模服务 |
总结
- 开发环境:优先使用代理服务器(如 Vue/React 的 devServer 配置)。
- 生产环境:推荐 CORS 或 Nginx 反向代理。
- 实时通信:使用 WebSocket。
- 遗留系统:JSONP 或 PostMessage。
根据具体场景选择最合适的方案,避免过度设计。