cookie cookie cookie !
Opened this issue · 0 comments
参考资料
本地测试环境搭建
弄了一个有前端代码和后端代码的仓库来在本地进行各种测试,见 https://github.com/findxc/frontend-and-backend-playground 。
通过 npm run start-test-cookie
来启动前后端服务,需要先本地生成 SSL 证书哦,详见本文底部。
cookie 的使用场景
参见 Using HTTP cookies - HTTP | MDN 。
Cookies are mainly used for three purposes:
Session management
Logins, shopping carts, game scores, or anything else the server should rememberPersonalization
User preferences, themes, and other settingsTracking
Recording and analyzing user behavior
以登录来举例,前端在登录页面发起一个登录请求,然后服务端在 response headers 中通过 set-cookie 来设置 cookie 的值,这之后前端在发起请求时浏览器会自动在 request headers 中携带上 cookie ,服务端校验 cookie 是有效的则返回相应数据给前端。
关于通过 cookie 来记录和分析用户行为参见 你是如何被广告跟踪的? - 知乎 。
set-cookie 的参数解释
在浏览器控制台中可以看到当前这个域下的所有 cookie ,如果有多个就有多行,每个 cookie 都有一些属性值。允许不同 cookie 设置不同属性值。
Name 和 Value 就是某个 cookie 的名字和值。
Domain 指这个 cookie 是和哪个 Domain 相关的,比如当请求 some.com 的资源时:
- 如果不设置 Domain ,那么默认为 some.com ,不包含子域名,也就是请求 a.some.com 的资源时不会携带 cookie ;
- 如果设置 Domain 为 some.com ,那么包含子域名,当请求 a.some.com 的资源时也会携带 cookie ;
- 不能设置 Domain 为非 some.com 的值,比如设为 a.some.com 是无效的;
- 如果是请求 a.some.com 的资源,可以设置 Domain 为 some.com ,也就是子域名可以设置 cookie 到主域上,但是主域名不能设置 cookie 到子域或者其它域上。
Path 指请求的 url 中需要包含的路径。
Expires / Max-Age 指 cookie 过期时间,如果不设的话默认值是 session ,当完全退出浏览器时(关闭掉这个应用进程,不仅仅是关闭浏览器窗口)会清除掉 cookie 。(如果说浏览器有自动恢复关闭窗口的功能,也可能不会清除掉 cookie ,所以一般还是手动设置一个过期时间比较好)
HttpOnly 如果设为 true ,那么前端不能通过 document.cookie 来访问该 cookie 值,如果前端没有访问 cookie 的必要,建议都加上该属性,避免 cookie 泄漏。
Secure 如果设为 true ,那么一定要通过 HTTPS 来请求资源才会携带 cookie / 才能 set-cookie 成功(localhost 例外)。
SameSite 如果不设,默认值为 Lax ,也可以设为 Strict 和 None 。
- Strict:只有请求的发送方和接收方位于同一个 site 时,才会携带 cookie / 才能 set-cookie 成功;
- Lax:在 Strict 基础上,从其它站点导航到当前站点时,发送的当前站点的请求也会携带 cookie ,比如从 b.com 通过 a 链接打开 a.com 时,a.com 发的请求如果 cookie 是 Strict 那么不会携带 cookie ,如果是 Lax 就会携带 cookie ;
- None:跨站时也允许携带 cookie 和 set-cookie 。 SameSite 设为 None 的话需要 Secure 同时设为 true ,否则无效。
比如 GitHub 的 cookie 绝大部分是 Lax ,当你已经登录了 GitHub 后,然后你从其它网站通过链接又打开 GitHub 时,由于允许携带 cookie ,所以你现在依然是登录状态。而如果说 cookie 是 Strict ,那么由于请求 github.com 时没携带 cookie 那么页面会是未登录的。
再举个例子,YouTube 的 cookie 是 None ,为啥呢?
因为 YouTube 有个功能是允许视频嵌入在其它网站中,用 iframe 的形式来播放,那么为了当你已经登录过 YouTube 后,在其它网站看到 YouTube 视频时也能一键添加到「稍后再看」,就需要 cookie 是允许跨站的,比如说 Introducing Zero-Bundle-Size React Server Components – React Blog 里面的 YouTube 视频。
SameParty 和 Priority 是 Chrome 的属性,暂未普及,不细说了。
关于 Cookie prefixes
在 Using HTTP cookies - HTTP | MDN 有介绍。
在上面 GitHub 和 YouTube 的 cookie 里可以看到有名字以 __Secure-
和 __Host-
开头的,它们是有特殊含义的。
__Secure-
开头的 cookie 必须同时设置 Secure 为 true ,也就是这类 cookie 肯定是通过 HTTPS 设置上的。
__Host-
开头的 cookie 必须同时设置 Secure 为 true 并且不设置 Domain ,也就是这类 cookie 不会说是子域名设置上的,只可能是当前服务端域设置上的。
如果说你恰好有某些 cookie 是希望确定就是自己这个域名设上的,那就可以试试 __Host-
。(但是看 GitHub 和 YouTube 好像没咋用这个属性 🤔 )
本地测试一下
首先得弄几个不同的域名,mac 下在终端中执行 sudo vi /etc/hosts
,然后在文件末尾补充:
127.0.0.1 aaa.com
127.0.0.1 bbb.com
127.0.0.1 some.com
127.0.0.1 a.some.com
127.0.0.1 b.some.com
这样当访问 aaa.com 其实就是访问 127.0.0.1 ,也就是 localhost 了。
然后再弄个 SSL 证书,这样本地启动的服务也可以使用 HTTPS 访问了。
使用 mkcert 来生成证书,安装好后,执行 mkcert -key-file ssl-key.pem -cert-file ssl-cert.pem localhost aaa.com bbb.com some.com a.some.com b.some.com
来生成证书,在本地启动前端/后端服务时配置上证书就能用 HTTPS 来访问了。
比如用 serve 来启动前端, 那么命令就类似于这样: serve —ssl-cert ./ssl-cert.pem —ssl-key ssl-key.pem your_dir
。
比如用 express 写的后端,那么代码就类似于下面:
// 支持 http 访问的服务
http.createServer(app).listen(PORT)
const keyPath = path.resolve(__dirname, '../ssl-key.pem')
const certPath = path.resolve(__dirname, '../ssl-cert.pem')
// 如果有 ssl 证书,那么再启动一个支持 https 访问的服务
if (fs.existsSync(keyPath)) {
const options = {
key: fs.readFileSync(keyPath),
cert: fs.readFileSync(certPath),
}
https.createServer(options, app).listen(PORT + 1)
}
ok,然后就可以开始愉快地各种测试了,把 cookie 的各个属性都测试一遍就懂啦。