当加上 CORS 之后,从发起到请求正式成功一般会经历以下过程:
简单请求(以 GET、POST 等简单方法且无特殊请求头的请求为例)
-
发起请求:客户端(通常是浏览器)向跨域的服务器发送请求,例如使用
fetch
或XMLHttpRequest
等 API。请求头中会包含Origin
字段,它表示请求发起的源,包括协议、域名和端口号。 -
服务器接收请求:服务器接收到请求后,会检查
Origin
字段。然后根据自身的 CORS 配置,判断该源是否被允许访问。 -
服务器响应:如果
Origin
是允许的,服务器会在响应头中添加Access - Control - Allow - Origin
字段,其值为允许的源(可以是具体的源,也可能是*
表示允许所有源)。同时,可能还会添加其他相关的 CORS 响应头,如Access - Control - Allow - Credentials
(如果涉及到跨域携带凭证)等。然后将响应数据发送给客户端。 -
客户端处理响应:浏览器接收到响应后,检查
Access - Control - Allow - Origin
等 CORS 相关头信息。如果这些头信息表明请求是被允许的,浏览器就会正常处理响应数据,将其传递给前端应用程序进行进一步处理和展示。
复杂请求(如 PUT、DELETE 方法或带有自定义请求头的请求)
发起预检请求(OPTIONS) :客户端首先会发送一个预检请求,使用
OPTIONS
方法。请求头中包含Origin
字段,同时还会有Access - Control - Request - Method
字段,用于告知服务器实际请求将使用的方法(如PUT
、DELETE
等),以及Access - Control - Request - Headers
字段,列出实际请求中会包含的自定义请求头。服务器接收预检请求:服务器接收到预检请求后,根据自身的 CORS 配置,检查
Origin
以及Access - Control - Request - Method
和Access - Control - Request - Headers
字段,判断是否允许该请求。服务器响应预检请求:如果允许,服务器会在响应头中添加
Access - Control - Allow - Origin
字段,指定允许的源。同时,还会添加Access - Control - Allow - Methods
字段,列出允许的请求方法;Access - Control - Allow - Headers
字段,列出允许的请求头。此外,还可能有Access - Control - Max - Age
字段,指定预检请求的结果可以被缓存的时长(以秒为单位)。客户端处理预检响应:浏览器接收到预检响应后,检查 CORS 相关头信息。如果预检请求被允许,浏览器会继续发起实际的请求。
发起实际请求:客户端按照正常流程发起实际的跨域请求,方法和请求头与预检请求中声明的一致。
服务器接收和响应实际请求:服务器接收实际请求,处理并返回响应,响应头中同样会包含
Access - Control - Allow - Origin
等 CORS 相关头信息,客户端根据这些头信息来决定是否处理响应数据。
在整个过程中,CORS 机制通过请求头和响应头的交互,让服务器能够明确告知浏览器哪些跨域请求是被允许的,从而实现了在遵循同源策略的基础上,安全地进行跨域资源访问。
怎么用CORS实现跨域
以下是使用 CORS(跨域资源共享) 实现跨域的完整方案,涵盖服务端配置、前端请求细节及常见问题处理:
一、CORS 核心机制
CORS 通过服务端设置 HTTP 响应头,告知浏览器允许特定页面的跨域请求。浏览器根据响应头决定是否放行跨域请求。
二、服务端配置
1. 基础响应头设置
在服务端响应中添加以下头信息(以 Node.js 为例):
// Express 中间件示例
app.use((req, res, next) => {
// 允许的源(根据需求替换为具体域名)
res.setHeader('Access-Control-Allow-Origin', 'https://your-frontend.com');
// 允许的请求方法
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
// 允许的请求头(需明确列出)
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
// 允许携带 Cookie(如需)
res.setHeader('Access-Control-Allow-Credentials', 'true');
// 预检请求缓存时间(秒)
res.setHeader('Access-Control-Max-Age', '86400');
next();
});
2. 处理预检请求(OPTIONS)
对于非简单请求(如 Content-Type: application/json
),浏览器会先发送 OPTIONS
预检请求:
// 单独处理 OPTIONS 请求
app.options('*', (req, res) => {
res.sendStatus(200);
});
三、前端请求配置
1. 简单请求(Simple Request)
满足以下条件时,浏览器直接发送请求:
-
方法为
GET
/POST
/HEAD
-
头信息仅含:
Accept
、Accept-Language
、Content-Language
、Content-Type
(值限于text/plain
、multipart/form-data
、application/x-www-form-urlencoded
)
示例:
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data));
2. 复杂请求(需预检)
对于其他请求(如 PUT
、自定义头、Content-Type: application/json
):
fetch('https://api.example.com/data', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer xxx'
},
body: JSON.stringify({ key: 'value' })
});
3. 携带 Cookie
需前后端协同设置:
// 前端请求添加 credentials
fetch('https://api.example.com/data', {
credentials: 'include' // 或 'same-origin'
});
// 服务端设置
res.setHeader('Access-Control-Allow-Credentials', 'true');
// 注意:此时 Access-Control-Allow-Origin 不能为 *
五、常见问题与调试
1. 错误排查
-
预检失败:检查
OPTIONS
响应头是否完整 -
头信息不匹配:确保
Access-Control-Allow-Headers
包含所有请求头 -
Cookie 未携带:检查前端
credentials
设置和服务端Allow-Credentials
2. 浏览器控制台警告
-
No 'Access-Control-Allow-Origin' header
:服务端未正确设置允许的源 -
Credentials not supported
:服务端使用Access-Control-Allow-Origin: *
但前端尝试携带 Cookie
3. 工具验证
使用 curl
检查响应头:
curl -I -X OPTIONS https://api.example.com/data
# 检查返回的 CORS 头信息
六、安全最佳实践
-
限制允许的源:避免使用
Access-Control-Allow-Origin: *
,应指定具体域名 -
缩小方法范围:按需设置
Access-Control-Allow-Methods
-
验证请求头:明确列出允许的请求头(避免使用
*
) -
限制缓存时间:合理设置
Access-Control-Max-Age
(避免过长)
通过以上配置,CORS 可安全高效地实现跨域请求,是现代 Web 应用的首选方案。