概述
cookie 是什么? cookie 是浏览器和 Web 服务器之间的媒介,通过用户请求在两者之间传递一些不太重要但具有标记作用的信息。
为什么需要 cookie? 众所周知,HTTP 是无状态的,如果 Web 浏览器想要记录用户当前会话等,就可以通过 cookie 传递数据。
cookie 有哪些限制? cookie 定位为一小段文本信息,一般大小不超过 4K。每个浏览器对于每个域名下 cookie 个数的限制也不同,但类似 Safari,个数就不受到限制。如果超出限制,有可能无法写入或老 cookie 被剔除。
cookie 怎么用? 综上所述,cookie一般用于存储少量不重要数据(cookie
过于庞大,会使得 HTTP 请求头变得臃肿,存储隐私数据有被窃取的风险),由 Web 服务器写入浏览器,浏览器会在以后每次 Web 请求中带入请求头中。
cookie 的类型
cookie 分为 session cookie 和 持久化 cookie。区别在于过期时间和用途,前者当用户退出登录或关闭浏览器时即消失,用于记录用户登录状态;后者用于存储其他不重要信息,过期时间较长,当用户手动清除或者到达过期时间时才消失。
cookie 的首部
cookie 有两个版本 - 版本 0 和版本 1,现代浏览器普遍使用版本 0 (由 Netscape 公司提出),故此处只介绍版本 0 。
我们先来看 chrome 浏览器下 baidu.com 的 cookie。
注意到共有 9 个字段,分别代表含义如下:
在介绍个字段具体内容之前我们先来看 Web 服务器向浏览器发送 cookie 的数据格式:
Set-Cookie: userId=123456;
domain='a.baidu.com';
path=/auto;
expires=2017-06-17T23:40:56.000Z;
secure=true|false;
sameSite=Lax|Strict
userId 即该 cookie 的名称,对应的值为 123456
,不过不建议对数据明文展示,还是要对 cookie 进行加密。
domain 即该 cookie 的作用域名。域名分为多级,如果设置 domain=baidu.com
,那么访问 baidu.com
后缀时都会发送该 cookie,如果设置 domain=tieba.baidu.com
,那么在访问 baidu.com
时则不会带上该 cookie。
path 即作用路径,类似 domain
的逻辑,与之共同作用。设置 domain=tieba.baidu.com; path=/
,访问 tieba.baidu.com
下所有的路径,都会把该 cookie 带入请求头。如果设置 domain=tieba.baidu.com; path=/gk
,则访问 tieba.baidu.com/gk
时才会发送。
expires/max-age 即过期时间。此处就对应前文说到的两种 cookie 类型。expires 的值是一个时间戳,max-age 的值是到过期时间的时间长度,以秒为单位。为什么会有两个值呢?原因在于,有时候客户端和服务器端的时间不一致,设置具体的时间点并没有意义。这个字段的值经常和缓存搭配使用,后面会说到。
size 即 cookie 的大小,这个字段通常是浏览器自己填写的,不需要用户干涉。
http 即该 cookie 是否在 http 访问时发送,对应 secure
字段 -- 是否只允许在 SSL 访问时发送。
samesite 目前只有 Chrome 和 FireFox 支持,是谷歌开发的一种安全机制,用于阻止 CSRF 和 XSS 攻击的一个手段,具体就不展开了,详见 参考[4]。
注意:关于域名分级又是一个话题,有时间可以另开一文详述
cookie 的工作原理
讲了这么久的废话,终于步入正题了。所以,cookie 到底是如何工作的呢?请看下图,<u>浏览器每次向服务端发起请求时,都会同时传递符合条件的所有 cookie 值</u>,所以不建议在 cookie 中放置较大的数据,这样请求会变得臃肿,影响传输效率:
cookie 的增删改查
新增/查询
浏览器本身不提供 cookie 的操作接口,为了方便操作,通常会把 cookie 操作封装起来。原始状态通过 document.cookie
能拿到的是所有没有设置 HTTP
字段的 cookie 值:
console.log(document.cookie);
"name1=value1; name2=value2"
在 浏览器端 设置 cookie 的操作是把一个 cookie 字符串赋值给 document.cookie
对象:
const cookie = 'token=123456; path=/writer; max-age=0';
document.cookie = cookie;
// 'token=123456; path=/writer; max-age=0'
console.log(document.cookie);
//'name1=value1; ..., token=123456'
在 服务器端 向 浏览器端 设置 cookie 是通过 响应首部 完成的:
javascript
Set-Cookie: name=value; max-age=1000;
#### 删除/更新
需要对 cookie 对象进行更新操作时,在 **浏览器端** 通过覆写键值对完成:
```javascript
document.cookie = 'token=12345';
console.log(document.cookie);
// 'token=12345'
document.cookie = 'token=23456';
console.log(document.cookie);
// 'token=23456'
删除操作只需要将 expires
字段或者 max-age
字段设置为立即过期或者过去时间即可:
document.cookie = 'token=123456';
console.log(document.cookie);
// 'token=12345'
document.cookie = 'token='',max-age=0';
// or
document.cookie = 'token='',expires=Thu, 29 Jun 2017 15:55:06 GMT';
console.log(document.cookie);
// ''
服务器端 同理,�同样是 Set-Cookie
首部,设值规则同浏览器端一致。
Cookie 与会话
由于 Cookie 在每次请求中都会传递到服务器端,所以一般会存储 Session 标识,每次访问会带上 session_id 等标记用户登录态的信息,服务端验证存在才可以进行进一步操作,否则判断为未登录。下图中的 session_id 的 cookie 就可以认为是会话保持关键字。
Cookie 与缓存
Cookie 的 max-age
和 expires
字段的值不为空且有效时,即响应头中有 Set-Cookie
字段时,响应的主体即文件资源等默认是可以进行缓存的,而这个缓存控制需要配合 no-control
等字段进行,具体另开一文详述。
参考
[1]. cookie 限制
[2]. 细说 Cookie
[3]. 再见,CSRF:讲解set-cookie中的SameSite属性
[4]. <HTTP 权威指南>
[5]. <图解 HTTP>
[6] <JavaScript 高级程序设计>