CrossOrigins Web跨域问题详解

Code & Dev
web

跨域问题在Web开发中算是老生常谈,也是最基础的问题了。一般在前后端分离架构的项目中常会出现。

跨域(Cross-Origin)通常指 浏览器中的同源策略(Same-Origin Policy)限制
当协议(http/https)、域名、端口任意一个不同,就属于“不同源”,浏览器会对资源访问进行限制。

比如前端fetch后端API,会报错:

Access to fetch at 'https://api.xxx.com' from origin 'http://localhost:3000' 
has been blocked by CORS policy

后端配置Response Header即可:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type

预检 OPTIONS 也要放行。登录请求多半是 JSON,会触发预检(OPTIONS)。

不过还有一个问题很多教程都没有提到,或者默认读者了解。 Access-Control-Allow-Origin 只能设置一个域。如果要设置多个Allow Origin,需要在后端加上判断逻辑,动态返回 Origin:

const allowList = [
  'https://a.com',
  'https://b.com'
]

const origin = req.headers.origin
if (allowList.includes(origin)) {
  res.setHeader('Access-Control-Allow-Origin', origin)
}

Cookies 的跨域问题

很常见的一个情况是:前端(app.lycois.org) POST 登录接口(api.lycois.org/auth/login),response 200里包含了Set-Cookie: token=…,但是客户端请求api.lycois.org/auth/me却没有携带cookie。或者说cookie没有设置成功。

前后端允许凭证

在前端的跨域请求中,如果要允许携带 凭证credentials,就要设置Access-Control-Allow-Credentials: true

fetch("https://api.xxx.com", {
  credentials: "include"
})

什么是 credentials?在浏览器里,Cookies,HTTP 认证信息(Basic Auth,Authorization header,TLS 客户端证书都是 credentials。

所以没有 include/withCredentials:cookie 不会发送、也不会保存

后端 CORS 也必须允许凭证。另外要注意的是:此时,Access-Control-Allow-Origin 不能是 *(带 cookie 必须精确域名)。

现代浏览器比较严格,跨站Cookie必须要以这种形式:

Set-Cookie: session=xxx; Path=/; HttpOnly; Secure; SameSite=None
  • SameSite=None:允许跨站发送 cookie(跨域登录的关键)
  • Secure:要求 https(没有 https,SameSite=None 的 cookie 会被浏览器拒收)
  • HttpOnly:防止 JS 读取 cookie(更安全)

本地开发

fallback

, but also send Authorization: JWT  if login returned a token