跨域问题在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 属性必须适配跨站(SameSite)
现代浏览器比较严格,跨站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