Skip to main content

跨域

跨域(CORS):前端通信的安全与突破

一、同源策略与跨域问题

同源策略(Same-Origin Policy)
浏览器默认限制页面从一个源(协议、域名、端口三者相同)加载的资源访问另一个源的资源。

  • 同源示例
    https://example.com/a.jshttps://example.com/b.js
  • 跨域示例
    https://api.example.com/datahttps://www.example.com

跨域限制

  • AJAX 请求:被浏览器阻止(如 fetchXMLHttpRequest)。
  • Cookie、LocalStorage:无法跨域访问。
  • DOM 操作:不同源的页面无法互相操作。

二、跨域解决方案

1. CORS(跨域资源共享)

原理:服务器通过响应头声明允许哪些源访问资源。
关键响应头

  • Access-Control-Allow-Origin:允许的源(如 https://example.com*)。
  • Access-Control-Allow-Methods:允许的 HTTP 方法(如 GET, POST)。
  • Access-Control-Allow-Headers:允许的请求头(如 Content-Type)。
  • Access-Control-Allow-Credentials:是否允许携带凭证(如 Cookie)。

服务器配置示例(Node.js)

const express = require('express');
const app = express();

app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', 'https://example.com');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
res.setHeader('Access-Control-Allow-Credentials', 'true');
next();
});

前端请求示例

fetch('https://api.example.com/data', {
credentials: 'include' // 携带 Cookie
})
.then(response => response.json())
.catch(error => console.error('CORS error:', error));

2. JSONP(JSON with Padding)

原理:利用 <script> 标签不受同源策略限制的特性。
步骤

  1. 前端创建回调函数(如 handleData)。
  2. 请求 URL 中添加回调函数名(如 ?callback=handleData)。
  3. 服务器返回包裹在回调函数中的 JSON 数据(如 handleData({"name":"John"}))。

示例

<script>
function handleData(data) {
console.log('Received data:', data);
}
</script>
<script src="https://api.example.com/data?callback=handleData"></script>

局限性

  • 仅支持 GET 请求。
  • 安全性较低(易受 XSS 攻击)。
  • 错误处理困难。

3. 代理服务器

原理:在同源的服务器上设置代理,转发请求到目标服务器。
示例(Node.js 代理)

const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');

const app = express();

// 将 /api 请求代理到 https://api.example.com
app.use('/api', createProxyMiddleware({
target: 'https://api.example.com',
changeOrigin: true,
pathRewrite: { '^/api': '' }
}));

app.listen(3000);

前端请求

// 访问同源的代理路径
fetch('/api/data')
.then(response => response.json());

4. WebSocket(不受同源限制)

原理:WebSocket 协议(ws://wss://)不遵循同源策略。
示例

const socket = new WebSocket('wss://api.example.com/socket');

socket.onmessage = (event) => {
console.log('Received message:', event.data);
};

5. postMessage(跨窗口通信)

原理:用于不同窗口(如 iframe、弹出窗口)之间的通信。
发送方

// 在 https://example.com 页面中
const iframe = document.querySelector('iframe');
iframe.contentWindow.postMessage('Hello from parent!', 'https://iframe.example.com');

接收方

// 在 https://iframe.example.com 页面中
window.addEventListener('message', (event) => {
if (event.origin === 'https://example.com') {
console.log('Received message:', event.data);
}
});

三、CORS 高级配置

1. 预检请求(Preflight Request)

  • 对于复杂请求(如 PUTDELETE,自定义请求头),浏览器会先发送 OPTIONS 请求确认权限。
  • 服务器需正确响应 OPTIONS 请求:
    app.options('/api/data', (req, res) => {
    res.setHeader('Access-Control-Allow-Origin', 'https://example.com');
    res.setHeader('Access-Control-Allow-Methods', 'PUT, DELETE');
    res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
    res.sendStatus(200);
    });

2. 携带凭证(Credentials)

  • 若需发送 Cookie 或 HTTP 认证,需同时设置:
    // 前端
    fetch('https://api.example.com', {
    credentials: 'include'
    });

    // 后端
    res.setHeader('Access-Control-Allow-Credentials', 'true');
    res.setHeader('Access-Control-Allow-Origin', 'https://example.com'); // 不能用 *

四、安全注意事项

  1. CORS 安全配置

    • 避免使用 Access-Control-Allow-Origin: * 处理敏感资源。
    • 精确控制允许的域名和请求头。
  2. JSONP 风险

    • 验证 callback 参数,防止 XSS 攻击。
    // 服务器端验证 callback 参数
    const callback = req.query.callback;
    if (!/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(callback)) {
    throw new Error('Invalid callback name');
    }
  3. 代理服务器安全

    • 限制代理的目标域名,避免成为通用代理。
    • 验证客户端请求,防止被利用攻击第三方服务。

五、工具与框架配置

1. Express.js(Node.js)

const cors = require('cors');

// 允许所有源
app.use(cors());

// 仅允许特定源
app.use(cors({
origin: 'https://example.com',
credentials: true
}));

2. Nginx 代理配置

server {
listen 80;
server_name example.com;

location /api/ {
proxy_pass https://api.example.com/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;

# 添加 CORS 头
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
}
}

六、总结:选择合适的跨域方案

场景方案优点缺点
现代浏览器,API 支持CORS标准方法,支持所有 HTTP 方法需要服务器配合
兼容性要求高,仅 GET 请求JSONP兼容性好仅支持 GET,安全性低
前后端同源部署困难代理服务器完全可控,安全性高需要额外服务器资源
实时通信WebSocket双向通信,无同源限制需服务器支持 WebSocket
跨窗口通信postMessage安全可靠仅适用于窗口间通信

在实际开发中,优先考虑 CORS 方案,若服务器无法修改,再考虑代理服务器或其他替代方案。