cookie知识总结
2021/2/21 5:10:55
本文主要是介绍cookie知识总结,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
最近,清除cookie的时候,使用了document.cookie = ''
,然后就发现没有作用。于是,带着上述疑问,找个时间研究了下cookie。本文主要是基于我遇到问题,然后解决问题的思路写的,并不是以从0到1讲述cookie的思路写的。
为了解决上述问题,以及在解决问题中的发散思考,主要通过以下问题:
document.cookie = ''
能否用来清除cookie;- 同时设置了expires和max-age,哪个有效;
- 假设已经有一个名为hello的cookie,再次设置名为hello的cookie是新增还是修改;
- 如何把字符串形式的cookie改成json的形式;
- 如果设置了多个同名cookie,获取cookie时以哪个为准;
学习了以下知识点:
- cookie赋值语法;
- cookie赋值算法;
- cookie取值算法。
预备知识
本文参考了英文文档,所以提前说明以下英文对应的中文翻译:
- cookie store: cookie表,浏览器内部用于存储cookie信息的数据结构;
- cookie name: 一个cookie的名称;
- cookie value: 一个cookie的值;
- cookie attribute: 一个cookie的属性,cookie除了基本的名称和值之外,还有用来进一步描述cookie的属性,比如Expires,Max-Age,Domain,Path,Secure HttpOnly等。这些属性是提供给用户设置cookie的时候使用的,并不是cookie内部实际存储的属性。
其他预备知识:
每个cookie在实际存储的时候,会有下述字段:name, value, expiry-time, domain, path, creation-time, last-access-time, persistent-flag, host-only-flag, secure-only-flag, and http-only-flag。可以看出这些字段和设置cookie时的属性并不是一对一关系,其中,
- expiry-time是通过Expires和Max-Age得出来的;
- persistent-flag和是否设置了过期时间有关;
- creation-time(创建时间)和last-access-time(最后一次访问时间)是浏览器自动存储的属性。
document.cookie赋值语法
这部分内容主要是基于以下问题:
document.cookie = ''
能否用来清除cookie?
首先看下document.cookie的定义:
TheDocument
propertycookie
lets you read and write cookies associated with the document. It serves as a getter and setter for the actual values of the cookies.
从上面可以看到,document.cookie只是提供了一个getter和setter方法来访问和设置cookie,并不是直接修改cookie的值。什么意思呢?
我们知道,JS提供了两种类型的属性,data property和accessor property。分别举个例子对比看下:
// data property var obj1 = { hello: 'name' } Object.getOwnPropertyDescriptor(obj1, 'hello')
// accessor property var realHello = '' // 实际存储obj2.hello的变量 var obj2 = { get hello () { return 'custom get ' + realHello }, set hello (val) { realHello = 'custom set ' + val } } obj2.hello = 'name' console.log(obj2.hello) Object.getOwnPropertyDescriptor(obj2, 'hello')
可以看到用于描述obj1
的hello
属性的是value和writable,用于描述obj2
的hello
属性的是get
和set
。简单的说,获取和修改data property
是直接的,获取和修改accessor property
是间接的,可以通过该属性的get
和set
做自定义处理。我们熟悉的Vue 2.0响应式的data的属性就是accessor property。
document的cookie属性也是accessor property。验证的时候发现一个有意思的问题,最终是在document的原型上找到的属性描述符,以后有时间了研究下:
Object.getOwnPropertyDescriptor(document, 'cookie') // undefined Object.getOwnPropertyDescriptor(document.__proto__.__proto__, 'cookie')
我们看下document.cookie的语法:
document.cookie = newCookie;
修改的时候,newCookie的格式需要满足如下形式,且一次只能设置/更新单个cookie:
In the code above,newCookie
is a string of formkey=value
. Note that you can only set/update a single cookie at a time using this method.
举个例子,打开MDN document.cookie页面。添加一个cookie,每个cookie后面可以添加和该cookie相关的一系列属性,通过分号;
分割:
// cookie默认过期时间是session,也就是浏览器关闭的时候,该cookie就失效了 document.cookie = 'hello1=world1;' // 使用max-age设置过期时间:max-age以秒为单位,设置1个小时之后过期:3600 = 60 * 60 document.cookie = 'hello2=world2;max-age=3600' // 使用expires设置过期时间:expires是GMT形式的日期格式,设置1小时之后过期 var current = new Date() current.setTime(current.getTime() + 3600000) // setTime单位是ms,3600000 = 60 * 60 * 1000 document.cookie = `hello3=world3;expires=${current.toUTCString()}`
Chrome Application Cookies内容截图如下:
既然document.cookie只能设置/更新单个cookie,当赋值为空字符串的时候,那就是什么都没有做。也就是document.cookie = ''
不会产生任何作用,不能用于清除cookie。
那么,
问:如何使用document.cookie清除一个cookie呢?
答:可以在设置cookie的时候,设置一个已经过去的过期时间:
// 使用expires设置一个过去一小时之前的时间 document.cookie = 'hello2=world2;max-age=-3600' // 使用expires设置一个过去一小时之前的时间 var current = new Date() current.setTime(current.getTime() - 3600000) document.cookie = `hello3=world3;expires=${current.toUTCString()}`
现在,只剩下了一个hello1:
注:浏览器开发者工具是提供了清除cookie的操作的,上述主要讨论的是如何通过js清除cookie。
cookie赋值算法
这部分内容主要是基于以下两个问题:
- 同时设置了expires和max-age,哪个有效?
- 假设已经有一个名为hello的cookie,再次设置名为hello的cookie的时候是新增还是修改?
问题1: 同时设置了expires和max-age,哪个有效?
前面的例子中,我使用了expires和max-age,我的疑问是感觉这两个属性是干了一样的事情,如果我同时设置了这两个属性的话,哪个生效呢?
带着这个疑问,找到了RFC 6265 - HTTP State Management Mechanism。这个规范里面明确定义了如何设置/更新cookie。
就下面的例子,我们按照文档找一下答案:
var current = new Date() console.log('now: ', current.toUTCString()) // now: Sat, 20 Feb 2021 11:41:02 GMT current.setTime(current.getTime() + 3600000) // 使用expires设置为一小时之后过期:60 * 60 * 1000 = 3600000ms // 使用max-age设置为两小时之后过期:2 * 60 * 60 = 7200s document.cookie = `hello=world;expires=${current.toUTCString()};max-age=7200` // hello=world;expires=Sat, 20 Feb 2021 12:41:02 GMT;max-age=7200
这部分的内容主要是在第五部分的5.2。首先,它会把设置的这个值通过逗号分割成多个部分,然后通过等号把每个部分分成key-value的形式:
hello=world;expires=Sat, 20 Feb 2021 12:41:02 GMT;max-age=7200 // 变为 hello=world // 第一个分号前面的是这个cookie的name和value,后面的是这个cookie的属性 expires=Sat, 20 Feb 2021 12:41:02 GMT // 属性 max-age=7200 // 属性 // 变为 hello: world expires: expires=Sat, 20 Feb 2021 12:41:02 GMT max-age: 7200
然后看每个属性,文档中5.2.*部分,算法会解析每个key-value,并放在一个cookie属性列表cookie-attribute-list里面,分析属性的时候不区分大小写,expires和max-age转化为:
Expires: expires=Sat, 20 Feb 2021 12:41:02 GMT Max-Age: expires=Sat, 20 Feb 2021 13:41:02 GMT
属性都分析完之后,我们可以得到和每条cookie相关的三个部分:cookie-name(cookie名),cookie-value(cookie值),cookie-attribute-list(cookie属性列表)。然后进入设置阶段,关键在5.3部分的第三步:
首先看上面生成的cookie属性列表里面是否包含Max-Age,如果包含,把cookie的过期时间设置成最后一个Max-Age的值;如果没有Max-Age,才会查看是否包含Expires,如果包含,把cookie的过期时间设置成最后一个Expires的值。这里last attribute的意思是我们在给cookie赋值的时候,是可以写多个max-age的,这个时候取最后一个语法有效的max-age。
所以,简单的说:当expires和max-age同时出现的时候,max-age的优先级更高。
Chrome截图也证明了max-age优先级更高:
假设已经有一个名为hello的cookie,再次设置名为hello的cookie是新增还是修改?
我们接着往下看5.3部分的第11、12步:
在11步中,首先判断已有cookie中是否有和本cookie同时具有相同的name(名称),domain(域),path(路径)的cookie。
11.1 如果有,标记为old-cookie
;
11.2 如果新cookie不是通过http的方式设置的,并且old-cookie设置了只能用于http,忽略新cookie;
11.3 把新cookie的creation-time(创建时间)字段更新为old-cookie的创建时间;
11.4 从cookie表中移除old-cookie;
12 把新cookie插入到cookie表里面。
所以,可以得出:判断是新增还是修改的标准是cookie表中是否存在和新cookie的name,domain和path属性都一样的cookie。
在设置cookie的时候,如果设置结果和你想要的不符,可以按照上面例子的思路,查看RFC 6265 - HTTP State Management Mechanism文档的5.2和5.3部分。
cookie取值算法
这部分内容主要是基于以下两个问题:
- 如何把字符串形式的cookie改成json的形式?
- 如果设置了多个同名cookie,获取cookie时以哪个为准?
如何把字符串形式的cookie改成json的形式?
之所以会遇到这个问题,主要是解决下面这个场景:
如果通过WebSocket发送请求,在连接建立的时候,是可以获取cookie信息的。但是,一旦连接建立成功,WebSocket一直连接的时候,发送的数据是不会携带cookie信息的。如果在连接建立之后,用户做了登出操作,这个时候其实不应该再响应用户请求,并且断开连接。
这时,问题来了,如何判断当用户登出的时候不再响应用户请求呢?
其实和http一样,每次请求带上cookie信息就可以了。只不过发送http请求的时候,浏览器帮我们做了这部分工作。WebSocket的话,就得我们手动添加上了。
我们可以通过document.cookie获取cookie值,但是该值是一个表示所有cookie的字符串。为了获取某个cookie的值,我们就得自己解析。要想解析,就得知道cookie字符串的取值规则。这部分内容主要是RFC 6265 - HTTP State Management Mechanism文档的5.4部分。
- 通过步骤4的第2步,可以知道不同的cookie是通过分号和空格
;
拼接起来的; - 通过步骤4的第1步,可以知道最终结果中只会出现cookie的name和value,cookie的属性并不会出现。name和value是通过等号
=
拼接起来的;
所以,解析cookie的为代码可以表示如下:
var cookieStr = document.cookie var cookieArray = cookieStr.split('; ') // 获得每个cookie的字符串形式组成的数组 var cookieObj = {} cookieArray.forEach(item => { var index = item.indexOf('=') // 获取每个cookie字符串的key和value if (index !== -1) { // 没有使用split的原因是value里面也是可以包含=号的 cookieObj[item.slice(0, index)] = item.slice(index + 1) } }) console.log(cookieObj)
如果设置了多个同名cookie,获取cookie时以哪个为准?
例如,有下面几个cookie:
document.cookie = 'hello4=world4;' document.cookie = 'hello5=world;' document.cookie = 'hello5=worldShortPath;Path=/en-US' document.cookie = 'hello6=world6;'
Chrome截图如下:
这部分内容还是在RFC 6265 - HTTP State Management Mechanism文档的[5.4部分],只不过是在第2步。首先第1步是从cookie store(cookie列表)中找到和请求地址相关的所有cookie。然后执行第2步:
- 通过步骤2的第1步,可以知道具有更长path属性的cookie是放在前面的;
- 通过步骤2的第2步,可以知道path属性长度相同的时候,creation-times创建时间越早的放在前面。
综上,所有同名的cookie都会出现在最终的cookie字符串中。并且,cookie是先按照path属性就行排序,然后按照creation-times创建时间属性进行排序。但是,步骤二下面有注释,这种排序方式是实践得出的比较好的排序方式,但是浏览器不一定按照这种排序方式实现。
就上面的例子,我们在设置完cookie之后,在控制台中查看document.cookie:
console.log(document.cookie) // hello4=world4; hello5=world; hello6=world6; hello5=worldShortPath
可以看到hello5的路径最短,所以放在最后,其他的按照设置顺序,也就是创建时间排序。
总结
在cookie赋值语法部分,我们知道document.cookie = ''
不能用来清除cookie;
在cookie赋值算法部分,我们知道:
- 同时设置了expires和max-age,max-age有效;
- 假设已经有一个名为hello的cookie,再次设置名为hello的cookie的时候,如果name,domain和path属性完全相同就是修改,否则是新增;
在cookie取值算法部分,我们知道:
- 如何把字符串形式的cookie改成json的形式的时候,可以先通过
;
分割一次,在通过=
分割一次; - 如果设置了多个同名cookie,所有同名的cookie都会出现在最终的cookie字符串中,按照path属性的长度和creation-times排序。
如果你遇到和cookie赋值语法、赋值算法和取值算法相关的问题,可以参照上述部分遇到的问题的解决思路去看看能否解决问题。
如有错误,欢迎留言讨论。
参考链接
- RFC 6265 - HTTP State Management Mechanism
- MDN document.cookie
这篇关于cookie知识总结的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-01前端项目部署入门:新手必读指南
- 2024-11-01富文本编辑器学习:从入门到初步掌握
- 2024-11-01前端项目部署学习:从入门到实践
- 2024-11-01动态主题处理:WordPress新手指南
- 2024-11-01前端项目部署指南:从零开始的部署教程
- 2024-11-01Element-Plus入门指南:轻松开始你的前端项目
- 2024-11-01TagsView标签栏导航入门教程
- 2024-11-01富文本编辑器课程:新手入门教程
- 2024-10-31前端项目部署课程:从入门到实践指南
- 2024-10-31用Angular实现服务器端渲染以提升SEO效果