最近,清除cookie的時候,使用了document.cookie = ''
,而後就發現沒有做用。因而,帶着上述疑問,找個時間研究了下cookie。本文主要是基於我遇到問題,而後解決問題的思路寫的,並非以從0到1講述cookie的思路寫的。html
爲了解決上述問題,以及在解決問題中的發散思考,主要經過如下問題:算法
document.cookie = ''
可否用來清除cookie;學習瞭如下知識點:json
本文參考了英文文檔,因此提早說明如下英文對應的中文翻譯:segmentfault
其餘預備知識:數組
每一個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時的屬性並非一對一關係,其中,瀏覽器
這部份內容主要是基於如下問題:cookie
document.cookie = ''
可否用來清除cookie?首先看下document.cookie的定義:session
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。分別舉個例子對比看下:dom
// 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。
這部份內容主要是基於如下兩個問題:
前面的例子中,我使用了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優先級更高:
咱們接着往下看5.3部分的第十一、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部分。
這部份內容主要是基於如下兩個問題:
之因此會遇到這個問題,主要是解決下面這個場景:
若是經過WebSocket發送請求,在鏈接創建的時候,是能夠獲取cookie信息的。可是,一旦鏈接創建成功,WebSocket一直鏈接的時候,發送的數據是不會攜帶cookie信息的。若是在鏈接創建以後,用戶作了登出操做,這個時候其實不該該再響應用戶請求,而且斷開鏈接。
這時,問題來了,如何判斷當用戶登出的時候再也不響應用戶請求呢?
其實和http同樣,每次請求帶上cookie信息就能夠了。只不過發送http請求的時候,瀏覽器幫咱們作了這部分工做。WebSocket的話,就得咱們手動添加上了。
咱們能夠經過document.cookie獲取cookie值,可是該值是一個表示全部cookie的字符串。爲了獲取某個cookie的值,咱們就得本身解析。要想解析,就得知道cookie字符串的取值規則。這部份內容主要是RFC 6265 - HTTP State Management Mechanism文檔的5.4部分。
;
拼接起來的;=
拼接起來的;因此,解析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:
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步:
綜上,全部同名的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賦值算法部分,咱們知道:
在cookie取值算法部分,咱們知道:
;
分割一次,在經過=
分割一次;若是你遇到和cookie賦值語法、賦值算法和取值算法相關的問題,能夠參照上述部分遇到的問題的解決思路去看看可否解決問題。
若有錯誤,歡迎留言討論。