常规跨域携带cookie的方式
在很多文章中经常能看到一种跨域携带cookie的方式是设置接口请求时withCredentials属性的方法,大致流程如下:
1.设置withCredentials: true
axios.default.withCredentials = true
// var xhr = new XMLHttpRequest()
// xhr.withCredentials = true
2.服务端配置跨域时的部分响应头字段 首先是最常规的,跨域需要设置的Access-Control-Allow-Origin,其次是跨域时允许携带cookie的Access-Control-Allow-Credentials,以nodejs的框架express为例
app.all("*", (req, res, next) => {
res.header("Access-Control-Allow-Origin", "http://xxx")
res.header("Access-Control-Allow-Credentials", "true")
next()
})
可是实际开发中,你可能会发现,即使这样设置了,你的接口请求依然有很大概率是不会携带cookie的。
这是因为chrome从51版本开始,对于cookie管理新增了samesite属性。
chrome及大部分现代浏览器
目前我尝试过大部分现在的主流浏览器chrome,firefox,safari甚至包括360和遨游。现在都全面支持了SameSite属性。
SameSite的值分成三种Strict, Lax, None。
1. Strict 完全禁止第三方 Cookie,跨站点时,任何情况下都不会发送 Cookie。
2. Lax 允许部分第三方请求携带 Cookie。
3. None 无论是否跨站都会发送 Cookie。
详情参见下图:
图片来源:zhuanlan.zhihu.com/p/266282015
并且目前主流浏览器都默认设置cookie.samesite = Lax。
那么是否意味着你将samesite设置成None就可以解决跨域问题了呢?也不尽然,参考MDN上的解释中的第二条:
- 如果
SameSite未指定,则SameSite=Lax时新的默认值。以前,默认情况下会为有请求发送 cookie。SameSite=None的 cookie 还必须指定Secure属性(它们需要安全上下文)。- cookie 使用不同的方案(
http:或https:)发送来自同一域的 cookie,则不再视为来自同一站点。
SameSite为None时,Secure必须为true,也就是此时你必须使用https请求。
到了这一步,你终于可以将自己的跨域请求携带上cookie了。
在91版本之前,你可以在chrome://flags/中可视化的对SameSite by default cookies进行关闭。
后来chrome移除了这一选项,你只能通过命令行关闭。在命令行或快捷方式启动chrome的参数中添加--disable-features=SameSiteByDefaultCookies后缀即可。
某一个版本以后命令行这一参数也被取消。目前你只能乖乖遵守SameSite这一属性的拷打了。或许你也可以尝试下载一个旧版本的chrome,或者提供一个chrome插件,并且强制你的用户也安装上
如何解决问题
设置cookie的SameSite = None; Security = true并使用https请求。(tips:注意cookie的path)
写在最后
在解决完问题之后我们不禁要问自己一个问题,既然现代浏览器大费周折地防止跨域携带cookie带来的安全问题,那么真的有必要跨域携带cookie吗?是否可以通过请求头的Authorization完成跨域鉴权呢?
实际上如果没有跨域名的无感登录,也许通过cookie鉴权真的不是一个很好的方案。