需求场景
一个Vue单页应用,A、B、C 三个页面都引用了一个公用的时间选择器。用户在各自页面选择完时间后,A,B,C页面互相切换时保存选择的时间,在关闭浏览器tab后,清除选择的时间,恢复初识值。一开始的想法是使用会话 cookie(不手动设置过期时间),但是会话 cookie 在关闭浏览器 tab 的时候不会被清除。所以决定研究下会话 cookie 何时清除。
cookie
背景
HTTP 是一种无状态的协议,它不对请求和响应之间的通信状态进行保存,即无法根据之前的状态进行本次请求的处理。假如要求登录验证的 web 页面本身无法进行状态的管理,那么每次跳转新页面就要再次登录,或者要在请求报文中附加参数来管理登录状态。无状态协议有它的优点,由于不必保存状态,可以减少服务器的 CPU 及内存资源。为了保留无状态协议这个特征的同时又要解决类似的矛盾问题,于是引入了 cookie。
原理
cookie 技术通过在请求和响应报文中写入 cookie 信息来控制客户端的状态。cookie 会根据从服务器端发送的响应报文内的一个叫做 Set-Cookie 的首部字段信息,通知客户端保存。当下次客户端再往该服务器发送请求时,客户端会自动在请求报文中加入 cookie 值后发送出去。
服务端发现客户端发送过来的 cookie 后,会去检查究竟是从哪一个客户端发来的连接请求,然后对比服务器上的记录,最后得到之前的状态信息。
-
客户端设置
// 属性之间由一个分号和一个空格隔开
document.cookie = 'name=value; expires=date; path=path'// 设置多个cookie
document.cookie = 'name=irene; domain=www.baidu.com';
document.cookie = 'age=18; path=/welcome';
document.cookie 一次只能设置一条 cookie,如果需要设置多条,需要多次设置。客户端可以设置的属性有:expires、max-age、domain、path、secure(https协议下才能设置成功),不能设置 HttpOnly。
服务器端设置
Set-Cookie: name=value[; expires=date][; domain=domain][; path=path][; secure]
// 设置多个cookie
Set-Cookie: name=irene; domain=www.baidu.com;
Set-Cookie: age=18; path=/welcome;
一个 Set-Cookie 只能设置一条 cookie,如果需要设置多条,需要多次使用 Set-Cookie。cookie 除了设置名称和值之外,还可以设置其他的属性。服务端可以设置的属性有:expires、max-age、domain、path、secure、HttpOnly 等。
属性
expires 和 max-age
设置 cookie 的失效时间。expires 的值是一个时间点(cookie 失效时刻 = expires),max-age 的值是秒数(cookie 失效时刻 = 创建时刻 + max-age)。expires 是 http/1.0 协议中的选项,在新的 http/1.1 协议中 expires 已经由 max-age 选项代替。如果两者同时存在,max-age 的优先级高于 expires。max-age 有两种可能值:小于等于0:有效期为能表示的最早时间;大于0:有效期为当前时间 + max-age。
// expires
document.cookie='name=irene; expires=Wed, 13 Jun 2019 10:28:27 GMT'
// max-age > 0
document.cookie='name=irene; max-age=3600'
// max-age <= 0
document.cookie='name=irene; max-age=0'
注意:一旦 cookie 从服务器端发送至客户端,服务器端就不存在可以显式删除 cookie 的方法。但可通过覆盖 cookie,实现对客户端 cookie 的删除操作。
domain & path
Domain 设置域名,默认是当前域名。path 设置路径,默认是当前目录。domain 和 path 一起限制了哪些请求可以带上该 cookie。比如上面第二个的 cookie,若请求的 URL 的域名是 juejin.com 或 xxx.juejin.com,并且 URL 的路径是 / 或 子路径 '/home',则浏览器会将此 cookie 添加到该请求的 cookie 头部中。
// 在 https://juejin.im/welcome/frontend 设置如下 cookie,则浏览器 Cookie 面板显示的 domain=juejin.im,path=/welcome
document.cookie = 'name=irene'
// 在 https://juejin.im/welcome/frontend 设置如下 cookie,则浏览器 Cookie 面板显示的 domain=.juejin.im(有前缀.),
document.cookie = 'name=irene; domain=juejin.im; path=/welcome/frontend'
// 关于 domain 有无前缀点可参考 http://www.it1352.com/548425.html
HttpOnly
当设置了 HttpOnly,浏览器就不会将该 cookie 添加到非 HTTP 请求的头部。
设置 cookie 是否能通过 js 去访问(读取、修改、删除等)。默认情况下,cookie 不会带 HttpOnly 选项,所以客户端是可以通过 js 代码去访问这个 cookie 的。当 cookie 带 HttpOnly 选项时,客户端则无法通过 js 代码去访问这个cookie。主要目的是为防止跨站脚本攻击对 cookie 的信息窃取。HttpOnly 只能从服务端设置,不能通过客户端设置。
Secure
设置仅在 HTTPS 安全连接时,才可以发送 cookie。
浏览器对于是否携带 cookie 项的策略
domain-matching
一个字符串(str)匹配(domain-matches)一个给定的域名字符串(domain str)至少需要满足以下条件中的一个:
- str 和 domain str 完全相同(转成小写后比较)。
-
或者同时满足以下条件:
2.1 domain str 是 str 的后缀。
2.2 str 中最后一个不包含在 domain str 中的字符是点(.)。domain str = google.com; str = map.googole.com => 符合 domain str = google.com; str = map.mgoogle.com => 最后一个不包含在 domain str 中的字符是 m,不符合
2.3 str 必须是一个域名,而不是 IP 地址。
path-matching
一个请求路径(request-path)匹配(path-matches)一个给定的 cookie-path 至少需要满足以下条件中的一个:
- request-path 和 cookie-path 完全一样。
- cookie-path 是 request-path 的前缀,并且 cookie-path 的最后一个字符是 / 。
- cookie-path 是 request-path 的前缀,并且 request-path 中第一个不包含在 cookie-path 中的字符是 / 。
注意:domain-matching & path-matching 只是浏览器决定是否携带 cookie 诸多参考项中的其中两个,浏览器还会参考诸如是否过期等项。
实践
问题:
cookie 不设置失效时间的话,默认是会话结束失效,这个会话结束是指浏览器的所有窗口都关闭,还是说这个网站的页面全部关闭就可以了?
具体场景:
Chrome浏览器开了两个窗口A B,A打开了网站1的两个标签页(tab1 & tab2)和网站2的两个标签页(tab3 & tab4),B打开了网站1的两个标签页(tab1 & tab2)和网站2的两个标签页(tab3 & tab4),如果想网站1的cookie失效的话,是不是把窗口A B关于网站1的tab页关掉就行 还是 需要把浏览器的所有窗口都关闭?
实验对象:
https://segmentfault.com。它的 PHPSESSION 是会话结束失效,所以用来测试。
工具:
通过chrome://settings/cookies/detail?site=segmentfault.com查看该网站下的所有cookie。
结论:
浏览器的所有窗口都关闭,网站1的cookie(会话cookie)才失效。windows 在浏览器窗口都关闭的情况下,会退出程序,所以 cookie 会失效;mac 在浏览器窗口都关闭的情况下,不会退出程序,所以 cookie 不会失效,需要退出程序 cookie 才失效。
规范
如果一个 cookie 同时设置了 Max-Age 和 Expires 属性,Max-Age 的优先级更高。如果两个都没有设置,UA 会保存该 cookie 直到当前 会话结束。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。