CORS 跨域原理
浏览器跨域资源共享的工作机制
问题
解释 CORS(Cross-Origin Resource Sharing)跨域的工作原理。
解答
什么是跨域
当协议、域名、端口三者任一不同时,就产生跨域。浏览器的同源策略会阻止跨域请求。
http://example.com/page
https://example.com/page // 协议不同
http://api.example.com/page // 域名不同
http://example.com:8080/page // 端口不同
CORS 工作流程
CORS 通过 HTTP 头来告诉浏览器允许跨域访问。分为简单请求和预检请求两种情况。
简单请求
满足以下条件的是简单请求:
- 方法:GET、HEAD、POST
- 请求头仅包含:Accept、Accept-Language、Content-Language、Content-Type(仅限 text/plain、multipart/form-data、application/x-www-form-urlencoded)
// 前端发起简单请求
fetch('https://api.example.com/data', {
method: 'GET',
credentials: 'include' // 携带 cookie
})
// 请求头
GET /data HTTP/1.1
Origin: https://example.com
// 响应头
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Credentials: true
预检请求(Preflight)
不满足简单请求条件时,浏览器先发送 OPTIONS 请求进行预检。
// 前端发起非简单请求
fetch('https://api.example.com/data', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'X-Custom-Header': 'value'
},
body: JSON.stringify({ name: 'test' })
})
// 1. 预检请求
OPTIONS /data HTTP/1.1
Origin: https://example.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Content-Type, X-Custom-Header
// 2. 预检响应
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, X-Custom-Header
Access-Control-Max-Age: 86400 // 预检结果缓存时间(秒)
// 3. 预检通过后,发送实际请求
PUT /data HTTP/1.1
Origin: https://example.com
Content-Type: application/json
服务端配置示例
// Node.js Express
const express = require('express')
const app = express()
app.use((req, res, next) => {
// 允许的源,* 表示所有(不能与 credentials 同时使用)
res.setHeader('Access-Control-Allow-Origin', 'https://example.com')
// 允许的方法
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE')
// 允许的请求头
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization')
// 允许携带凭证(cookie)
res.setHeader('Access-Control-Allow-Credentials', 'true')
// 预检请求缓存时间
res.setHeader('Access-Control-Max-Age', '86400')
// 处理预检请求
if (req.method === 'OPTIONS') {
return res.sendStatus(204)
}
next()
})
# Nginx 配置
location /api {
add_header Access-Control-Allow-Origin https://example.com;
add_header Access-Control-Allow-Methods 'GET, POST, PUT, DELETE';
add_header Access-Control-Allow-Headers 'Content-Type, Authorization';
add_header Access-Control-Allow-Credentials true;
if ($request_method = OPTIONS) {
return 204;
}
}
CORS 响应头说明
| 响应头 | 说明 |
|---|---|
| Access-Control-Allow-Origin | 允许访问的源 |
| Access-Control-Allow-Methods | 允许的 HTTP 方法 |
| Access-Control-Allow-Headers | 允许的请求头 |
| Access-Control-Allow-Credentials | 是否允许携带凭证 |
| Access-Control-Max-Age | 预检结果缓存时间 |
| Access-Control-Expose-Headers | 允许前端访问的响应头 |
关键点
- 同源策略:协议、域名、端口必须完全相同
- 简单请求直接发送,非简单请求先发 OPTIONS 预检
- CORS 是服务端通过响应头控制的,前端无法绕过
Access-Control-Allow-Origin: *不能与credentials: true同时使用- 预检结果可通过
Access-Control-Max-Age缓存,减少 OPTIONS 请求
目录