跨域资源共享(CORS)的原理与实现
字数 1899 2025-11-18 04:16:25
跨域资源共享(CORS)的原理与实现
1. 问题描述
跨域资源共享(CORS)是一种机制,允许网页从不同域(协议、域名、端口任一不同)的服务器访问资源。浏览器的同源策略(Same-Origin Policy)默认禁止跨域请求,但实际业务中(如前后端分离架构)常需跨域访问资源,CORS 通过特定 HTTP 头实现安全的跨域通信。
2. CORS 的基本原理
CORS 的核心是浏览器与服务器通过 HTTP 头协商跨域权限。整个过程分为两类请求:
- 简单请求:直接发送跨域请求,服务器返回
Access-Control-Allow-Origin头授权。 - 预检请求:非简单请求需先发送
OPTIONS请求,验证通过后再发送实际请求。
2.1 简单请求的条件
满足以下所有条件时,浏览器会将其视为简单请求:
- 请求方法为
GET、POST或HEAD。 - 请求头仅包含以下字段:
Accept、Accept-Language、Content-Language、Content-Type(仅限application/x-www-form-urlencoded、multipart/form-data、text/plain)。
- 未使用自定义头(如
Authorization)或特殊头(如Range)。
2.2 简单请求的流程
- 浏览器直接发送跨域请求,并在请求头中自动添加
Origin(例如Origin: https://frontend.com)。 - 服务器检查
Origin:- 若允许该源,返回
Access-Control-Allow-Origin: https://frontend.com(或*表示允许所有源)。 - 若未返回该头,浏览器拦截响应。
- 若允许该源,返回
示例代码(Node.js):
// 服务器设置响应头
app.get("/data", (req, res) => {
res.setHeader("Access-Control-Allow-Origin", "https://frontend.com");
res.json({ data: "CORS allowed" });
});
3. 预检请求的流程
当请求不满足简单请求条件时(如使用 PUT 方法或自定义头),浏览器会先发送预检请求。
3.1 预检请求的触发条件
- 请求方法为
PUT、DELETE等非简单方法。 - 使用
Content-Type: application/json或自定义头(如X-API-Key)。
3.2 预检请求的步骤
- 浏览器发送
OPTIONS请求,包含以下头:Origin: https://frontend.comAccess-Control-Request-Method: PUT(声明实际请求方法)Access-Control-Request-Headers: X-API-Key(声明自定义头)
- 服务器响应预检请求,需返回以下头:
Access-Control-Allow-Origin: https://frontend.comAccess-Control-Allow-Methods: PUT, POST(允许的方法)Access-Control-Allow-Headers: X-API-Key(允许的自定义头)Access-Control-Max-Age: 86400(缓存预检结果,单位秒)
- 浏览器检查响应头:若符合要求,发送实际请求;否则报错。
示例代码(Node.js):
// 处理 OPTIONS 预检请求
app.options("/data", (req, res) => {
res.setHeader("Access-Control-Allow-Origin", "https://frontend.com");
res.setHeader("Access-Control-Allow-Methods", "PUT, POST");
res.setHeader("Access-Control-Allow-Headers", "X-API-Key");
res.setHeader("Access-Control-Max-Age", 86400);
res.status(204).send(); // 空响应
});
// 处理实际请求
app.put("/data", (req, res) => {
res.setHeader("Access-Control-Allow-Origin", "https://frontend.com");
res.json({ status: "Updated" });
});
4. 带凭证的请求
默认情况下,CORS 请求不携带 Cookie 或 HTTP 认证信息。若需传递凭证,需显式配置:
- 前端设置:在请求中启用
withCredentials(例如 Fetch API 设置credentials: "include")。 - 服务器响应头:
Access-Control-Allow-Origin必须指定具体域名(不能为*)。- 添加
Access-Control-Allow-Credentials: true。
示例代码:
// 前端
fetch("https://api.example.com/data", {
credentials: "include"
});
// 后端
app.get("/data", (req, res) => {
res.setHeader("Access-Control-Allow-Origin", "https://frontend.com");
res.setHeader("Access-Control-Allow-Credentials", "true");
res.json({ user: "authenticated" });
});
5. 常见问题与解决方案
- CORS 错误:检查服务器是否返回正确的
Access-Control-Allow-Origin。 - 凭证被拒绝:确保
Access-Control-Allow-Credentials: true且域名非通配符。 - 预检请求频繁:通过
Access-Control-Max-Age缓存减少预检次数。
6. 总结
CORS 通过浏览器与服务器的协作,在保障安全的前提下实现跨域资源访问。关键在于理解简单请求与预检请求的区别,并正确配置 HTTP 头。实际应用中,后端框架(如 Express、Spring)通常提供中间件或注解简化 CORS 配置。