跨域资源共享 (CORS) 与同源策略

1. 同源策略 (Same-Origin Policy)

浏览器为了安全性,默认禁止读取不同源的资源(包括 Cookie、DOM、LocalStorage 和 AJAX 请求)。 同源必须同时满足:协议 (protocol)域名 (domain)端口 (port) 完全一致。

违规示例

  • http://a.comhttps://a.com (协议不同)
  • http://a.comhttp://b.com (域名不同)
  • http://a.com:80http://a.com:8080 (端口不同)

2. CORS (Cross-Origin Resource Sharing)

CORS 是浏览器的安全机制,允许服务器声明“哪些源可以访问我的资源”。

CORS 请求分为两类:简单请求预检请求 (Preflight)

(1) 简单请求 (HEAD, GET, POST)

浏览器直接发送请求,带上 Origin: <当前源>。服务器响应头必须包含:

  • Access-Control-Allow-Origin: *<Origin>
  • Access-Control-Allow-Credentials: true (如果带 Cookie)

(2) 非简单请求 (如 PUT, DELETE, application/json)

浏览器必须先发送一个 OPTIONS 请求进行询问(预检)。

OPTIONS 请求头:

  • Access-Control-Request-Method: PUT
  • Access-Control-Request-Headers: content-type

服务器响应头:

  • Access-Control-Allow-Methods: GET, POST, PUT
  • Access-Control-Allow-Headers: content-type
  • Access-Control-Max-Age: 86400 (缓存预检结果,减少请求)

3. 面试高频问题:Cookie 跨域携带

默认情况下,跨域请求不携带 Cookie。如果需要携带(例如保持登录状态):

  1. 前端:AJAX/Fetch 设置 withCredentials: true
  2. 后端
    • Access-Control-Allow-Origin 不能*,必须写死具体的域名(如 http://localhost:3000)。
    • Access-Control-Allow-Credentials: true

4. JSONP (旧方案)

JSONP 利用 <script> 标签不受同源策略限制。

  • 原理:动态插入 <script src="http://api.com?callback=fn">,服务端返回JS代码 fn({ data: ... })
  • 缺点:只支持 GET 请求,不安全(XSS 风险)。

5. 其他解决方案

  • Nginx 反向代理:配置 proxy_pass 转发请求,浏览器以为请求的是同源服务器(实际上 Nginx 去请求了后端),彻底绕开浏览器同源策略
  • WebSocket: 不受同源策略限制。
  • postMessage: 前端跨窗口通信。

6. 面试总结

  • 核心:CORS 是浏览器行为,服务端只是配置响应头。
  • 区别:简单请求直接发,非简单请求发 OPTIONS。
  • 坑点:凭证请求(带 Cookie)服务端不能写 Allow-Origin: *