什么樣的數(shù)據(jù)適合放在cookie中?
cookie是怎么設(shè)置的?
cookie為什么會自動加到request header中?
cookie怎么增刪查改?
我們要帶著這幾個問題繼續(xù)往下看。
cookie 是怎么工作的?
首先必須明確一點,存儲cookie是瀏覽器提供的功能。cookie 其實是存儲在瀏覽器中的純文本,瀏覽器的安裝目錄下會專門有一個 cookie 文件夾來存放各個域下設(shè)置的cookie。
當(dāng)網(wǎng)頁要發(fā)http請求時,瀏覽器會先檢查是否有相應(yīng)的cookie,有則自動添加在request header中的cookie字段中。這些是瀏覽器自動幫我們做的,而且每一次http請求瀏覽器都會自動幫我們做。這個特點很重要,因為這關(guān)系到“什么樣的數(shù)據(jù)適合存儲在cookie中”。
存儲在cookie中的數(shù)據(jù),每次都會被瀏覽器自動放在http請求中,如果這些數(shù)據(jù)并不是每個請求都需要發(fā)給服務(wù)端的數(shù)據(jù),瀏覽器這設(shè)置自動處理無疑增加了網(wǎng)絡(luò)開銷;但如果這些數(shù)據(jù)是每個請求都需要發(fā)給服務(wù)端的數(shù)據(jù)(比如身份認(rèn)證信息),瀏覽器這設(shè)置自動處理就大大免去了重復(fù)添加操作。所以對于那設(shè)置“每次請求都要攜帶的信息(最典型的就是身份認(rèn)證信息)”就特別適合放在cookie中,其他類型的數(shù)據(jù)就不適合了。
但在 localStorage 出現(xiàn)之前,cookie被濫用當(dāng)做了存儲工具。什么數(shù)據(jù)都放在cookie中,即使這些數(shù)據(jù)只在頁面中使用而不需要隨請求傳送到服務(wù)端。當(dāng)然cookie標(biāo)準(zhǔn)還是做了一些限制的:每個域名下的cookie 的大小最大為4KB,每個域名下的cookie數(shù)量最多為20個(但很多瀏覽器廠商在具體實現(xiàn)時支持大于20個)
cookie 的格式
因為cookie本身就是存儲在瀏覽器中的字符串。但這個字符串是有格式的,由鍵值對 key=value構(gòu)成,鍵值對之間由一個分號和一個空格隔開。
cookie 的屬性選項
每個cookie都有一定的屬性,如什么時候失效,要發(fā)送到哪個域名,哪個路徑等等。這些屬性是通過cookie選項來設(shè)置的,cookie選項包括:expires、domain、path、secure、HttpOnly。在設(shè)置任一個cookie時都可以設(shè)置相關(guān)的這些屬性,當(dāng)然也可以不設(shè)置,這時會使用這些屬性的默認(rèn)值。在設(shè)置這些屬性時,屬性之間由一個分號和一個空格隔開。代碼示例如下:
"key=name; expires=Thu, 25 Feb 2016 04:18:00 GMT; domain=ppsc.sankuai.com; path=/; secure; HttpOnly"
expires
expires選項用來設(shè)置“cookie 什么時間內(nèi)有效”。expires其實是cookie失效日期,expires必須是 GMT 格式的時間(可以通過 new Date().toGMTString()或者 new Date().toUTCString() 來獲得)。
如expires=Thu, 25 Feb 2016 04:18:00 GMT表示cookie講在2016年2月25日4:18分之后失效,對于失效的cookie瀏覽器會清空。如果沒有設(shè)置該選項,則默認(rèn)有效期為session,即會話cookie。這種cookie在瀏覽器關(guān)閉后就沒有了。
expires 是 http/1.0協(xié)議中的選項,在新的http/1.1協(xié)議中expires已經(jīng)由 max-age 選項代替,兩者的作用都是限制cookie 的有效時間。expires的值是一個時間點(cookie失效時刻= expires),而max-age 的值是一個以秒為單位時間段(cookie失效時刻= 創(chuàng)建時刻+ max-age)。
另外,max-age 的默認(rèn)值是 -1(即有效期為 session );若max-age有三種可能值:負(fù)數(shù)、0、正數(shù)。負(fù)數(shù):有效期session;0:刪除cookie;正數(shù):有效期為創(chuàng)建時刻+ max-age
domain 和 path
domain是域名,path是路徑,兩者加起來就構(gòu)成了 URL,domain和path一起來限制 cookie 能被哪些 URL 訪問。
一句話概括:某cookie的 domain為“baidu.com”, path為“/ ”,若請求的URL(URL 可以是js/html/img/css資源請求,但不包括 XHR 請求)的域名是“baidu.com”或其子域如“api.baidu.com”、“dev.api.baidu.com”,且 URL 的路徑是“/ ”或子路徑“/home”、“/home/login”,則瀏覽器會將此 cookie 添加到該請求的 cookie 頭部中。
所以domain和path2個選項共同決定了cookie何時被瀏覽器自動添加到請求頭部中發(fā)送出去。如果沒有設(shè)置這兩個選項,則會使用默認(rèn)值。domain的默認(rèn)值為設(shè)置該cookie的網(wǎng)頁所在的域名,path默認(rèn)值為設(shè)置該cookie的網(wǎng)頁所在的目錄。
特別說明1:
發(fā)生跨域xhr請求時,即使請求URL的域名和路徑都滿足 cookie 的 domain 和 path,默認(rèn)情況下cookie也不會自動被添加到請求頭部中。若想知道原因請閱讀本文最后一節(jié))
特別說明2:
domain是可以設(shè)置為頁面本身的域名(本域),或頁面本身域名的父域,但不能是公共后綴 public suffix。舉例說明下:如果頁面域名為 www.baidu.com, domain可以設(shè)置為“www.baidu.com”,也可以設(shè)置為“baidu.com”,但不能設(shè)置為“.com”或“com”。
secure
secure選項用來設(shè)置cookie只在確保安全的請求中才會發(fā)送。當(dāng)請求是HTTPS或者其他安全協(xié)議時,包含 secure 選項的 cookie 才能被發(fā)送至服務(wù)器。
默認(rèn)情況下,cookie不會帶secure選項(即為空)。所以默認(rèn)情況下,不管是HTTPS協(xié)議還是HTTP協(xié)議的請求,cookie 都會被發(fā)送至服務(wù)端。但要注意一點,secure選項只是限定了在安全情況下才可以傳輸給服務(wù)端,但并不代表你不能看到這個 cookie。
httpOnly
這個選項用來設(shè)置cookie是否能通過 js 去訪問。默認(rèn)情況下,cookie不會帶httpOnly選項(即為空),所以默認(rèn)情況下,客戶端是可以通過js代碼去訪問(包括讀取、修改、刪除等)這個cookie的。當(dāng)cookie帶httpOnly選項時,客戶端則無法通過js代碼去訪問(包括讀取、修改、刪除等)這個cookie。
在客戶端是不能通過js代碼去設(shè)置一個httpOnly類型的cookie的,這種類型的cookie只能通過服務(wù)端來設(shè)置。
——httpOnly與安全
從上面介紹中,大家是否會有這樣的疑問:為什么我們要限制客戶端去訪問cookie?其實這樣做是為了保障安全。
試想:如果任何 cookie 都能被客戶端通過document.cookie獲取會發(fā)生什么可怕的事情。當(dāng)我們的網(wǎng)頁遭受了 XSS 攻擊,有一段惡意的script腳本插到了網(wǎng)頁中。這段script腳本做的事情是:通過document.cookie讀取了用戶身份驗證相關(guān)的 cookie,并將這些 cookie 發(fā)送到了攻擊者的服務(wù)器。攻擊者輕而易舉就拿到了用戶身份驗證信息,于是就可以搖搖大擺地冒充此用戶訪問你的服務(wù)器了(因為攻擊者有合法的用戶身份驗證信息,所以會通過你服務(wù)器的驗證)。
如何設(shè)置 cookie?
知道了cookie的格式,cookie的屬性選項,接下來我們就可以設(shè)置cookie了。首先得明確一點:cookie既可以由服務(wù)端來設(shè)置,也可以由客戶端來設(shè)置。
服務(wù)端設(shè)置 cookie
不管你是請求一個資源文件(如 html/js/css/圖片),還是發(fā)送一個ajax請求,服務(wù)端都會返回response。而response header中有一項叫set-cookie,是服務(wù)端專門用來設(shè)置cookie的。如下圖所示,服務(wù)端返回的response header中有5個set-cookie字段,每個字段對應(yīng)一個cookie(注意不能將多個cookie放在一個set-cookie字段中),set-cookie字段的值就是普通的字符串,每個cookie還設(shè)置了相關(guān)屬性選項。
注意:
服務(wù)端可以設(shè)置cookie 的所有選項:expires、domain、path、secure、HttpOnly
客戶端設(shè)置 cookie
在網(wǎng)頁即客戶端中我們也可以通過js代碼來設(shè)置cookie:
document.cookie = "name=Jonh; ";
注意:
客戶端可以設(shè)置cookie 的下列選項:expires、domain、path、secure(有條件:只有在https協(xié)議的網(wǎng)頁中,客戶端設(shè)置secure類型的 cookie 才能成功),但無法設(shè)置HttpOnly選項。