- Published on
三方cookie问题
- Author Avatar
- Author
- Author Link
要实现三方cookie的携带和写入,要注意几个响应头。和一个请求模式。
条件
- 服务器一域名 http://localhost:4321
- 服务器二域名: http://localhost:5502(取决于 live-server 的插件)
正常情况(同源)
同源。从 4321 打开网页,并且请求。 此时服务器代码为:
if (req.url.startsWith('/api')) {
// set cookie
res.setHeader('Set-Cookie', 'key=value;')
// 返回 JSON 响应
res.setHeader('Content-Type', 'application/json')
res.writeHead(200)
res.end(JSON.stringify({ succ: true, msg: 'hello world' }))
}
客户端请求代码:
cbtn.addEventListener('click', () => {
fetch('http://localhost:4321/api')
})
就是比较正常的情况。此时做请求结果如下:
第一次请求:
正常被设置了 cookie

第二次请求:
正常携带了cookie

场景
场景一:直接请求三方服务器(跨域请求)
首先解决跨域问题,再实现上面的一系列操作。
此时服务器代码:
if (req.url.startsWith('/api')) {
// cors
res.setHeader('Access-Control-Allow-Origin', '*')
// set cookie
res.setHeader('Set-Cookie', 'key=value;') // 确保格式正确
// 返回 JSON 响应
res.setHeader('Content-Type', 'application/json')
res.writeHead(200)
res.end(JSON.stringify({ succ: true, msg: 'hello world' }))
}
无论请求多少次,都无法正常设置cookie

但是本地写入cookie是没问题的。document.cookie="xx=xx"
解决
方法一: CORS (cross-origin-resource- sharing)
客户端
开启请求的 credentials 模式 允许请求携带凭证。
fetch('http://localhost:4321/api', { credentials: 'include' }) axios.get('xxxx', { // `withCredentials` 表示跨域请求时是否需要使用凭证 withCredentials: true, // default 为 false })
服务端
设置
Access-Control-Allow-Credentials
如果客户端请求设置了 credentials ,但是响应头没有Access-Control-Allow-Credentials
的话,会抛错 请求的 credentials 和 此响应头 是配套的res.setHeader('Access-Control-Allow-Credentials', 'true')
- 设置
Access-Control-Allow-Origin
以上CORS错误,当请求的 credentials mode 为 include 时。
Access-Control-Allow-Origin
不能为通配符。 所以,设置Access-Control-Allow-Origin
为被允许的站点。- res.setHeader('Access-Control-Allow-Origin', '*') + res.setHeader('Access-Control-Allow-Origin', 'http://localhost:5502')
方法二: 代理
做一个代理服务器,把例如 localhost:4321/trd-cookie 代理当前 5502 的服务器上。
场景二:iframe 嵌入
特殊情况
前言:
根据 gpt 所说,出现如下情况,是因为浏览器的宽松策略把 localhost 作为同源看待。或许只会出现在 localhost 上。
场景描述:
例如我在 5502 中嵌入了一个 4321 的网页。此时需要 4321 发送请求给 4321/api,此接口响应头会 Set-Cookie。
结果:
如上场景我发现。直接请求即可。直接 Set-Cookie 可以让 5502 和 4321 都能获取到 cookie。即使 SameSite 设置为 Strict
服务端代码:
if (req.url.startsWith('/api')) {
// set cookie
res.setHeader('Set-Cookie', 'key=value;SameSite=Strict;')
// 返回 JSON 响应
res.setHeader('Content-Type', 'application/json')
res.writeHead(200)
res.end(JSON.stringify({ succ: true, msg: 'hello world' }))
}
客户端代码:
// iframe 中
fetch('http://localhost:4321/api')
如下图,5502 也被正常设置到了 cookie,并且能够获取到这个 cookie

并且,5502在发送请求 (到 5502 的接口) 的时候,也可以正常携带 cookie

强模拟情况
使用 虚拟机 + 更改 host 来模拟这种情况
场景描述
父网页:
域名 ==> https://licuiivm.com
子项目:
域名 ==> http://localhost.com
请求 ==> http://localhost.com/api
可以看到如下情况

如下图所示,cookie 已经被添加了,但是会被屏蔽掉。并不会放入请求头中去

既然如此,修改 SameSite 的值试试。将 4321 服务器的 Set-Cookie 加上 SameSite=None。

如上,当 SameSite 设置为 None 时,必须使用 Secure。那么再次设置 Secure
如图,正常被设置并且第二次请求时可以正常携带。


此时服务端代码为:
if (req.url.startsWith('/api')) {
// set cookie
res.setHeader('Set-Cookie', 'key=value;SameSite=None;Secure;')
// 返回 JSON 响应
res.setHeader('Content-Type', 'application/json')
res.writeHead(200)
res.end(JSON.stringify({ succ: true, msg: 'hello world' }))
}
客户端代码:普通请求即可
fetch('http://localhost:4321/api')
拓展 Partitioned
虽然上述场景已经OK,但是还是有一个警告。

其解决的是一个分区的问题。如下场景:
- 一个用户访问
https://site-a.example
,该站点嵌入了https://3rd-party.example
的内容。https://3rd-party.example
在用户设备上设置了 cookie。 - 该用户访问
https://site-b.example
,该站点同样嵌入了https://3rd-party.example
的内容。 https://3rd-party.example
的新实例仍可以访问用户在上一页(site-a.example)时设置的 cookie。
有关 cookie 分区:Partitioned
总结
三方cookie获取的两种场景:
场景一:跨域资源请求 场景二:iframe获取
解决方案:
场景一:通过 CORS(cross-origin-resource-sharing) 进行跨域资源共享 场景二:通过 Set-Cookie 的 SameSite 和 Secure 进行设置
CORS
服务端
以下两个请求头必须设置正确:
- Access-Control-Allow-Credentials 此响应头和请求的 credentials[凭证] 模式挂钩。
Access-Control-Allow-Origin 也是和请求的 credentials 模式挂钩,当为 include 时。不能使用通配符。
客户端
如果希望向三方服务器获取 cookie,则必须开启 credentials(fetch)/ withCredentials(xhr)
credentials
是 Request
接口的只读属性,用于表示用户代理是否应该在跨域请求的情况下从其他域发送 cookies。这与 XHR 的 withCredentials 标志相似,不同的是有三个可选值(后者是两个Boolean):
-
omit
: 从不发送 cookies. -
same-origin
: 只有当 URL 与响应脚本同源才发送 cookies、HTTP Basic authentication 等验证信息.(浏览器默认值,在旧版本浏览器,例如 safari 11 依旧是 omit,safari 12 已更改) -
include
: 不论是不是跨域的请求,总是发送请求资源域在本地的 cookies、HTTP Basic authentication 等验证信息。
Iframe
iframe 嵌入,即使被嵌入的网页和请求的接口是同源的
如:iframe.src=http://localhost:4321.com,请求接口为 http://localhost:4321/api
也无法正常被设置cookie
此时,Set-Cookie 除了 cookie 值之外,还需要设置三个:
- SameSite=None,
- 默认为 Lax,意味着 cookie 不会在跨站请求中被发送,如:加载图像或框架(frame)的请求。
- 设置为 None 时,必须同时设置
Secure
属性,就像这样:SameSite=None; Secure
。
- Secure
- 表示仅当请求通过
https:
协议(localhost 不受此限制)
- 表示仅当请求通过
- Partitioned