新手應該了解的Cookie知識!
一、Cookie的出現
浏覽器和伺服器之間的通信少不了HTTP協定,但是因為HTTP協定是無狀态的,是以伺服器并不知道上一次浏覽器做了什麼樣的操作,這樣嚴重阻礙了互動式Web應用程式的實作。
二、Cookie的傳輸
伺服器端在實作Cookie标準的過程中,需要對任意HTTP請求發送Set-CookieHTTP頭作為響應的一部分:
1.Set-Cookie:name=value;expires=Tue,03-Sep-201914:10:21GMT;path=/;domain=.xxx.com;
浏覽器端會存儲這樣的Cookie,并且為之後的每個請求添加CookieHTTP請求頭發送回伺服器:
1.Cookie:name=value
伺服器通過驗證Cookie值,來判斷浏覽器發送請求屬于哪一個使用者。
三、浏覽器中的Cookie
浏覽器中的Cookie主要由以下幾部分組成:
·名稱:Cookie唯一的名稱,必須經過URL編碼處理。(同名會出現覆寫的情況)
·值:必須經過URL編碼處理。
·域(domain):預設情況下cookie在目前域下有效,你也可以設定該值來確定對其子域是否有效。
·路徑(path):指定Cookie在哪些路徑下有效,預設是目前路徑下。
·失效時間(expires):預設情況下,浏覽器會話結束時會自動删除Cookie;也可以設定一個GMT格式的日期,指定具體的删除日期;如果設定的日期為以前的日期,那麼Cookie會立即删除。
·安全标志(secure):指定之後隻允許Cookie發送給https協定。
浏覽器在發送請求時,隻會将名稱與值添加到請求頭的Cookie字段中,發送給服務端。
浏覽器提供了一個非常蹩腳的API來操作Cookie:
1.document.cookie
通過上述方法可以對該Cookie進行寫操作,每一次隻能寫入一條Cookie字元串:
1.document.cookie='a=1;secure;path=/'
通過該方法還可以進行Cookie的讀操作:
1.document.cookie//"a=1"
由于上述方法操作Cookie非常的不直覺,一般都會寫一些函數來簡化Cookie讀取、設定和删除操作。
對于Cookie的設定操作中,需要以下幾點:
對于名稱和值進行URL編碼處理,也就是采用JavaScript中的encodeURIComponent()方法;expires要求傳入GMT格式的日期,需要處理為更易書寫的方式,比如:設定秒數的方式;注意隻有的屬性名的secure;
每一段資訊需要采用分号加空格。
1.functionsetCookie(key,value,attributes){
2.if(typeofdocument==='undefined'){
3.return
4.}
5.attributes=Object.assign({},{
6.path:'/'
7.},attributes)
8.
9.let{domain,path,expires,secure}=attributes
10.
11.if(typeofexpires==='number'){
12.expires=newDate(Date.now()+expires*1000)
13.}
14.if(expiresinstanceofDate){
15.expires=expires.toUTCString()
16.}else{
17.expires=''
18.}
19.
20.key=encodeURIComponent(key)
21.value=encodeURIComponent(value)
22.
23.letcookieStr=
${key}=${value}
24.
25.if(domain){
26.cookieStr+=
;domain=${domain}
27.}
28.
29.if(path){
30.cookieStr+=
;path=${path}
31.}
32.
33.if(expires){
34.cookieStr+=
;expires=${expires}
35.}
36.
37.if(secure){
38.cookieStr+=
;secure
39.}
40.
41.return(document.cookie=cookieStr)
42.}
Cookie的讀操作需要注意的是将名稱與值進行URL解碼處理,也就是調用JavaScript中的decodeURIComponent()方法:
1.functiongetCookie(name){
5.letcookies=[]
6.letjar={}
7.document.cookie&&(cookies=document.cookie.split(';'))
9.for(leti=0,max=cookies.length;i
10.let[key,value]=cookies[i].split('=')
11.key=decodeURIComponent(key)
12.value=decodeURIComponent(value)
13.jar[key]=value
14.if(key===name){
15.break
16.}
17.}
18.
19.returnname?jar[name]:jar
20.}
最後一個清除的方法就更加簡單了,隻要将失效日期(expires)設定為過去的日期即可:
1.functionremoveCookie(key){
2.setCookie(key,'',{expires:-1})
3.}
介紹Cookie基本操作的封裝之後,還需要了解浏覽器為了限制Cookie不會被惡意使用,規定了Cookie所占磁盤空間的大小以及每個域名下Cookie的個數。
四、服務端的Cookie
相比較浏覽器端,服務端執行Cookie的寫操作時,是将拼接好的Cookie字元串放入響應頭的Set-Cookie字段中;執行Cookie的讀操作時,則是解析HTTP請求頭字段Cookie中的鍵值對。
與浏覽器最大的不同,在于服務端對于Cookie的安全性操碎了心
signed
當設定signed=true時,服務端會對該條Cookie字元串生成兩個Set-Cookie響應頭字段:
1.Set-Cookie:lastTime=2019-03-05T14:31:05.543Z;path=/;httponly
2.Set-Cookie:lastTime.sig=URXREOYTtMnGm0b7qCYFJ2Db400;path=/;httponly
這裡通過再發送一條以.sig為字尾的名稱以及對值進行加密的Cookie,來驗證該條Cookie是否在傳輸的過程中被篡改。
httpOnly
服務端Set-Cookie字段中新增httpOnly屬性,當服務端在傳回的Cookie資訊中含有httpOnly字段時,開發者是不能通過JavaScript來操縱該條Cookie字元串的。
這樣做的好處主要在于面對XSS(Cross-sitescripting)攻擊時,黑客無法拿到設定httpOnly字段的Cookie資訊。
此時,你會發現localStorage相比較Cookie,在XSS攻擊的防禦上就略遜一籌了。sameSite
在介紹這個新屬性之前,首先你需要明白:當使用者從
http://a.com發起
http://b.com的請求也會攜帶上Cookie,而從
攜帶過來的Cookie稱為第三方Cookie。
雖然第三方Cookie有一些好處,但是給CSRF(Cross-siterequestforgrey)攻擊的機會。
為了從根源上解決CSRF攻擊,sameSite屬性便閃亮登場了,它的取值有以下幾種:
·strict:浏覽器在任何跨域請求中都不會攜帶Cookie,這樣可以有效的防禦CSRF攻擊,但是對于有多個子域名的網站采用主域名存儲使用者登入資訊的場景,每個子域名都需要使用者重新登入,造成使用者體驗非常的差。
·lax:相比較strict,它允許從三方網站跳轉過來的時候使用Cookie。
為了友善大家了解sameSite的實際效果,可以看這個例子:
1.//a.com服務端會在通路頁面時傳回如下Cookie
2.cookies.set('foo','aaaaa')
3.cookies.set('bar','bbbbb')
4.cookies.set('name','cccccc')
5.
6.//b.com服務端會在通路頁面時傳回如下Cookie
7.cookies.set('foo','a',{sameSite:'strict'})
8.cookies.set('bar','b',{sameSite:'lax'})
9.cookies.set('baz','c')
如何現在使用者在a.com中點選連結跳轉到b.com,它的請求頭是這樣的:
1.RequestHeaders
2.
3.Cookie:bar=b;baz=c
五、網站性能優化
Cookie在服務端和浏覽器的通信中,主要依靠HTTP的響應頭和請求頭傳輸的,是以Cookie會占據一定的帶寬。
前面提到浏覽器會為每一次HTPP請求自動攜帶上Cookie資訊,但是對于同站内的靜态資源,伺服器并不需要處理其攜帶的Cookie,這無形中便浪費了帶寬。