跨域资源共享
跨域资源共享(CORS)是一种浏览器机制,允许网页从不同源(协议、域名或端口)的服务器请求资源。它是为了解决浏览器的同源策略(Same-Origin Policy)限制而设计的。
1. 什么是跨域?
跨域是指浏览器从一个源(Origin)向另一个源发起请求。同源策略要求协议、域名和端口完全相同,否则就是跨域。
- 同源示例
https://example.com和https://example.com/api是同源。
- 跨域示例
https://example.com和https://api.example.com是跨域(域名不同)。http://example.com和https://example.com是跨域(协议不同)。https://example.com和https://example.com:8080是跨域(端口不同)。
2. CORS 的工作原理
CORS 通过在 HTTP 请求和响应中添加特定的头部字段来实现跨域资源共享。
2.1 简单请求(Simple Request)
简单请求满足以下条件:
- 请求方法是
GET、POST或HEAD。 - 请求头只包含以下字段:
AcceptAccept-LanguageContent-LanguageContent-Type(值为application/x-www-form-urlencoded、multipart/form-data或text/plain)。
流程:
- 浏览器直接发送跨域请求。
- 服务器在响应头中添加
Access-Control-Allow-Origin,指定允许访问的源。 - 浏览器检查响应头,如果允许访问,则返回数据;否则,抛出错误。
示例:
-
请求:
GET /data HTTP/1.1 Host: api.example.com Origin: https://example.com -
响应:
HTTP/1.1 200 OK Access-Control-Allow-Origin: https://example.com Content-Type: application/json {"data": "Hello, CORS!"}
2.2 预检请求(Preflight Request)
如果请求不满足简单请求的条件(如使用 PUT 方法或自定义请求头),浏览器会先发送一个预检请求(OPTIONS 方法)来确认服务器是否允许跨域请求。
流程:
- 浏览器发送预检请求(OPTIONS)。
- 服务器响应预检请求,指定允许的请求方法、请求头和源。
- 如果预检请求通过,浏览器发送实际请求。
- 服务器响应实际请求。
示例:
-
预检请求:
OPTIONS /data HTTP/1.1 Host: api.example.com Origin: https://example.com Access-Control-Request-Method: PUT Access-Control-Request-Headers: Content-Type -
预检响应:
HTTP/1.1 204 No Content Access-Control-Allow-Origin: https://example.com Access-Control-Allow-Methods: PUT Access-Control-Allow-Headers: Content-Type -
实际请求:
PUT /data HTTP/1.1 Host: api.example.com Origin: https://example.com Content-Type: application/json {"data": "Hello, CORS!"} -
实际响应:
HTTP/1.1 200 OK Access-Control-Allow-Origin: https://example.com Content-Type: application/json {"data": "Request successful!"}
3. CORS 相关 HTTP 头部字段
3.1 请求头
Origin:表示请求的源(协议 + 域名 + 端口)。Access-Control-Request-Method:用于预检请求,表示实际请求的方法(如PUT)。Access-Control-Request-Headers:用于预检请求,表示实际请求的自定义头(如Content-Type)。
3.2 响应头
Access-Control-Allow-Origin:指定允许访问的源(如https://example.com或*)。Access-Control-Allow-Methods:指定允许的请求方法(如GET, POST, PUT)。Access-Control-Allow-Headers:指定允许的请求头(如Content-Type)。Access-Control-Allow-Credentials:指定是否允许发送凭据(如 Cookie)。Access-Control-Max-Age:指定预检请求的缓存时间(秒)。
4. CORS 的常见问题
4.1 跨域请求被阻止
- 原因:服务器未正确配置
Access-Control-Allow-Origin。 - 解决方法:确保服务器响应头中包含
Access-Control-Allow-Origin,并设置为允许的源或*。
4.2 预检请求失败
- 原因:服务器未正确处理预检请求(OPTIONS)。
- 解决方法:确保服务器支持 OPTIONS 方法,并正确返回
Access-Control-Allow-Methods和Access-Control-Allow-Headers。
4.3 跨域请求无法携带凭据
-
原因:默认情况下,跨域请求不会发送凭据(如 Cookie)。
-
解决方法
- 在请求中设置
credentials: 'include'(Fetch API)或withCredentials: true(XMLHttpRequest)。 - 服务器响应头中设置
Access-Control-Allow-Credentials: true。
- 在请求中设置
5. CORS 的示例代码
5.1 服务器端(Node.js + Express)
const express = require("express");
const app = express();
app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "https://example.com");
res.header("Access-Control-Allow-Methods", "GET, POST, PUT");
res.header("Access-Control-Allow-Headers", "Content-Type");
res.header("Access-Control-Allow-Credentials", "true");
next();
});
app.get("/data", (req, res) => {
res.json({ message: "Hello, CORS!" });
});
app.listen(3000, () => {
console.log("Server running on port 3000");
});
5.2 客户端(Fetch API)
fetch("https://api.example.com/data", {
method: "GET",
credentials: "include", // 允许发送凭据
})
.then((response) => response.json())
.then((data) => console.log(data))
.catch((error) => console.error("Error:", error));
6. 总结
- CORS 是一种浏览器机制,用于解决跨域资源共享问题。
- 简单请求直接发送,复杂请求需要先发送预检请求。
- 服务器通过设置响应头(如
Access-Control-Allow-Origin)来控制跨域访问。 - 合理配置 CORS 可以提升网站的安全性和兼容性。