06 CORS 的工作原理

1. CORS 解决的是什么问题

CORS(Cross-Origin Resource Sharing,跨源资源共享)是浏览器对同源策略的一种受控放行机制。

一个源由三部分组成:协议、主机、端口。任何一项不同就是跨源:

页面:http://localhost:5173
接口:http://localhost:8080
                    ^^^^ 端口不同,因此跨源

浏览器会把页面的源放入请求头:

Origin: http://localhost:5173

服务端通过响应头说明是否允许这个源。最终是否把响应交给 JavaScript,由浏览器决定。

CORS 不是阻止请求抵达服务器的防火墙。某些请求已经执行并返回 200,浏览器仍可能禁止 JavaScript 读取响应。

2. CORS、认证与 CSRF 必须分开

机制 回答的问题 典型手段
CORS 哪些网页源可以由浏览器读取响应 Access-Control-*
认证 调用者是谁 Session、JWT、API Key
授权 此人是否可以执行此操作 角色、权限、资源归属检查
CSRF 防护 浏览器是否被其他网站借用登录凭据发起操作 SameSite、CSRF Token、Origin 检查

curl、Postman、后端程序不执行浏览器同源策略,因此不会被 CORS 拦住。即使只允许公司前端域名,攻击者仍能直接调用 API;接口依旧必须认证和授权。

如果认证依赖浏览器自动携带的 Cookie,还要单独防范 CSRF。Bearer Token 通常由 JavaScript 显式加入 Authorization,攻击网站一般拿不到该 Token,但仍要防范 XSS 和 Token 泄漏。

3. 简单请求

满足浏览器规定的“简单请求”条件时,浏览器直接发送正式请求。例如:

GET /api/users HTTP/1.1
Host: api.example.com
Origin: https://www.example.com

服务端允许后返回:

Access-Control-Allow-Origin: https://www.example.com
Vary: Origin

常见简单方法只有 GETHEADPOST;请求头和 Content-Type 也受限制。application/x-www-form-urlencodedmultipart/form-datatext/plain 可能符合简单请求条件,application/json 不属于简单 Content-Type。

“简单请求”不等于安全请求。HTML 表单可以跨源提交 POST,所以有副作用的 Cookie 接口仍须防 CSRF。

4. 预检请求

以下情况常触发预检:

浏览器先自动发送 OPTIONS:

OPTIONS /api/users/1 HTTP/1.1
Origin: http://localhost:5173
Access-Control-Request-Method: PATCH
Access-Control-Request-Headers: authorization,content-type

服务端可返回:

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: http://localhost:5173
Access-Control-Allow-Methods: GET,POST,PATCH,DELETE
Access-Control-Allow-Headers: Authorization,Content-Type
Access-Control-Max-Age: 600
Vary: Origin, Access-Control-Request-Headers

浏览器确认允许后,才发送 PATCH。预检失败时,正式请求通常不会发送。

5. 用 curl 重现预检

curl -i -X OPTIONS http://127.0.0.1:8080/api/users/1 \
  -H "Origin: http://localhost:5173" \
  -H "Access-Control-Request-Method: PATCH" \
  -H "Access-Control-Request-Headers: authorization,content-type"

只发送 curl -X OPTIONS 不足以模拟浏览器预检,关键的三个 Header 也要提供。

6. 为什么 Postman 正常而浏览器失败

Postman 能看到 200,只证明网络和接口本身可能正常。浏览器还可能因为以下原因拒绝前端代码:

排错时同时查看浏览器 Console、Network 中的 OPTIONS 和正式请求,以及 Nginx/Express 日志。

7. 无 Origin 的请求

同源请求通常不需要 Origin;curl、健康检查、服务间调用也可能没有它。无 Origin 不表示恶意,也不表示可信。

常用策略是:

8. 本章结论

CORS 决定浏览器是否把响应交给页面 JavaScript
认证决定调用者是谁
授权决定调用者能做什么
CSRF 防护阻止其他网站借用浏览器凭据