Browser
同源策略与 CORS
什么是同源策略?什么是跨源资源共享(CORS)?
核心答案
**同源策略(Same-Origin Policy)**是浏览器的一个安全机制,限制一个源的文档或脚本如何与另一个源的资源进行交互。
同源策略
同源的定义:协议(Protocol)、域名(Domain)、端口(Port)三者完全相同。
示例:
https://www.example.com:443/page.html
协议:https
域名:www.example.com
端口:443
同源:
✅ https://www.example.com:443/other.html
✅ https://www.example.com:443/api/data
不同源:
❌ http://www.example.com:443/page.html(协议不同)
❌ https://api.example.com:443/page.html(域名不同)
❌ https://www.example.com:8080/page.html(端口不同)同源策略的限制:
- Cookie、LocalStorage、SessionStorage:只能访问同源的数据
- DOM 访问:不能访问不同源的 iframe 内容
- AJAX 请求:不能发送跨域请求(受 CORS 限制)
- WebSocket:不受同源策略限制(但可以限制)
跨源资源共享(CORS)
**CORS(Cross-Origin Resource Sharing)**是一种机制,允许服务器声明哪些源可以访问其资源。
CORS 的工作原理
1. 简单请求(Simple Request)
条件:
- 方法:GET、HEAD、POST
- 请求头:Content-Type 为
text/plain、application/x-www-form-urlencoded、multipart/form-data - 没有自定义请求头
流程:
浏览器 → 直接发送请求 → 服务器
浏览器 ← 检查响应头 Access-Control-Allow-Origin ← 服务器示例:
// 客户端
fetch('https://api.example.com/data');
// 服务器响应头
Access-Control-Allow-Origin: https://www.example.com2. 预检请求(Preflight Request)
触发条件:
- 方法:PUT、DELETE、PATCH 等
- 自定义请求头
- Content-Type 为
application/json
流程:
浏览器 → OPTIONS 预检请求 → 服务器
浏览器 ← 预检响应(CORS 头) ← 服务器
浏览器 → 实际请求 → 服务器
浏览器 ← 实际响应 ← 服务器示例:
// 客户端
fetch('https://api.example.com/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Custom-Header': 'value'
},
body: JSON.stringify({ data: 'test' })
});
// 预检请求(OPTIONS)
// 服务器响应
Access-Control-Allow-Origin: https://www.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, X-Custom-Header
Access-Control-Max-Age: 86400CORS 响应头
1. Access-Control-Allow-Origin
Access-Control-Allow-Origin: https://www.example.com
Access-Control-Allow-Origin: * # 允许所有源(不推荐,不安全)2. Access-Control-Allow-Methods
Access-Control-Allow-Methods: GET, POST, PUT, DELETE3. Access-Control-Allow-Headers
Access-Control-Allow-Headers: Content-Type, Authorization4. Access-Control-Allow-Credentials
Access-Control-Allow-Credentials: true- 允许发送 Cookie
- 不能与
Access-Control-Allow-Origin: *同时使用
5. Access-Control-Max-Age
Access-Control-Max-Age: 86400- 预检请求的缓存时间(秒)
延伸追问
1. 如何解决跨域问题?
回答:解决方案:
1. CORS(推荐)
// 服务器设置响应头
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', 'https://www.example.com');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
});2. JSONP(仅 GET 请求)
// 客户端
function handleData(data) {
console.log(data);
}
const script = document.createElement('script');
script.src = 'https://api.example.com/data?callback=handleData';
document.body.appendChild(script);
// 服务器返回
handleData({ data: 'test' });3. 代理服务器
// 开发环境:webpack devServer
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'https://api.example.com',
changeOrigin: true
}
}
}
};
// 生产环境:Nginx
location /api {
proxy_pass https://api.example.com;
}4. postMessage(iframe 通信)
// 父页面
iframe.contentWindow.postMessage('data', 'https://child.example.com');
// 子页面
window.addEventListener('message', (event) => {
if (event.origin === 'https://parent.example.com') {
console.log(event.data);
}
});2. CORS 预检请求什么时候发送?
回答:触发条件:
1. 非简单方法
fetch('https://api.example.com/data', {
method: 'PUT' // 触发预检
});2. 自定义请求头
fetch('https://api.example.com/data', {
headers: {
'X-Custom-Header': 'value' // 触发预检
}
});3. Content-Type 为 application/json
fetch('https://api.example.com/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json' // 触发预检
},
body: JSON.stringify({ data: 'test' })
});不触发预检:
// 简单请求
fetch('https://api.example.com/data'); // GET,不触发预检
fetch('https://api.example.com/data', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded' // 简单 Content-Type
},
body: 'data=test'
});3. 如何设置 CORS 允许携带 Cookie?
回答:设置方法:
客户端:
fetch('https://api.example.com/data', {
credentials: 'include' // 发送 Cookie
});服务器:
Access-Control-Allow-Origin: https://www.example.com # 不能是 *
Access-Control-Allow-Credentials: true注意:
Access-Control-Allow-Origin不能是*- 必须指定具体的源
credentials: 'include'和Access-Control-Allow-Credentials: true必须同时设置
4. CORS 错误如何排查?
回答:排查步骤:
1. 检查响应头
fetch('https://api.example.com/data')
.then(response => {
console.log(response.headers.get('Access-Control-Allow-Origin'));
});2. 查看浏览器控制台
- 查看错误信息
- 检查预检请求(Network 面板)
- 查看响应头
3. 常见错误
Access to fetch at '...' from origin '...' has been blocked by CORS policy- 原因:服务器未设置
Access-Control-Allow-Origin - 解决:服务器添加 CORS 响应头
Preflight request doesn't pass access control check- 原因:预检请求失败
- 解决:检查
Access-Control-Allow-Methods、Access-Control-Allow-Headers
5. CORS 和 JSONP 的区别?
回答:主要区别:
| 特性 | CORS | JSONP |
|---|---|---|
| 支持的方法 | 所有 HTTP 方法 | 仅 GET |
| 安全性 | 高(服务器控制) | 低(容易受 XSS 攻击) |
| 错误处理 | 完善的错误处理 | 难以处理错误 |
| 现代性 | 现代标准 | 传统方案 |
| 推荐度 | ✅ 推荐 | ❌ 不推荐 |
JSONP 的问题:
- 只能 GET 请求
- 安全性差(容易受 XSS 攻击)
- 错误处理困难
- 需要服务器支持回调
(注:文档部分内容可能由 AI 生成)