【其他學習】聊一聊 cookie,做大專案必懂的知識點
咱們不搞一開始就一大堆理論知識介紹,怕把人講懵了...... 咱們換一個思維方式——"從現象看本質",先說說我們看到了什麼,再從看到的現象中提出問題,最後深入尋找答案。
我們看到的 cookie
我自己建立了一個網站,網址為http://ppsc.sankuai.com
。在這個網頁中我設定了幾個cookie
:JSSESSIONID
,PA_VTIME
,skmtutc
,test
。
在 chrome 瀏覽器中開啟這個網站,進入開發者模式,點選Resources
欄 -> 選擇cookies
,我們會看到如下圖所示的介面:
解釋一下:左邊欄Cookies
下方會列舉當前網頁中設定過cookie
cookie
列表,對應上圖就是“ppsc.sankuai.com”域下設定的4個cookie
。
在這個網頁中我往http://ppsc.sankuai.com/getList
介面發了一個 Ajax 請求,request
header
如下圖所示:
從上圖中我們會看到request header
中自動添加了Cookie
欄位(我並沒有手動新增這個欄位哦~),Cookie
欄位的值其實就是我設定的那4個 cookie
。這個請求最終會發送到http://ppsc.sankuai.com
這個伺服器上,這個伺服器就能從接收到的request
header
cookie
。
上面兩張圖展示了cookie
的基本通訊流程:設定cookie
=> cookie
被自動新增到request
header
中 => 服務端接收到cookie
。這個流程中有幾個問題需要好好研究:
-
什麼樣的資料適合放在
cookie
中? -
cookie
是怎麼設定的? -
cookie
為什麼會自動加到request header
中? -
cookie
怎麼增刪查改?
我們要帶著這幾個問題繼續往下閱讀。
cookie 是怎麼工作的?
首先必須明確一點,儲存cookie
是瀏覽器提供的功能。cookie
其實是儲存在瀏覽器中的純文字,瀏覽器的安裝目錄下會專門有一個
cookie 資料夾來存放各個域下設定的cookie
當網頁要發http
請求時,瀏覽器會先檢查是否有相應的cookie
,有則自動新增在request
header
中的cookie
欄位中。這些是瀏覽器自動幫我們做的,而且每一次http
請求瀏覽器都會自動幫我們做。這個特點很重要,因為這關係到“什麼樣的資料適合儲存在cookie
中”。
儲存在cookie
中的資料,每次都會被瀏覽器自動放在http
請求中,如果這些資料並不是每個請求都需要發給服務端的資料,瀏覽器這設定自動處理無疑增加了網路開銷;但如果這些資料是每個請求都需要發給服務端的資料(比如身份認證資訊),瀏覽器這設定自動處理就大大免去了重複新增操作。所以對於那設定“每次請求都要攜帶的資訊(最典型的就是身份認證資訊)”就特別適合放在cookie
中,其他型別的資料就不適合了。
但在 localStorage 出現之前,cookie
被濫用當做了儲存工具。什麼資料都放在cookie
中,即使這些資料只在頁面中使用而不需要隨請求傳送到服務端。當然cookie
標準還是做了一些限制的:每個域名下的cookie
的大小最大為4KB,每個域名下的cookie
數量最多為20個(但很多瀏覽器廠商在具體實現時支援大於20個)。
cookie 的格式
document.cookie
JS 原生的 API提供了獲取cookie
的方法:document.cookie
(注意,這個方法只能獲取非
HttpOnly 型別的cookie
)。在 console 中執行這段程式碼可以看到結果如下圖:
打印出的結果是一個字串型別,因為cookie
本身就是儲存在瀏覽器中的字串。但這個字串是有格式的,由鍵值對 key=value
構成,鍵值對之間由一個分號
和一個空格
隔開。
cookie 的屬性選項
每個cookie
都有一定的屬性,如什麼時候失效,要傳送到哪個域名,哪個路徑等等。這些屬性是通過cookie
選項來設定的,cookie
選項包括:expires
、domain
、path
、secure
、HttpOnly
。在設定任一個cookie
時都可以設定相關的這些屬性,當然也可以不設定,這時會使用這些屬性的預設值。在設定這些屬性時,屬性之間由一個分號
和一個空格
隔開。程式碼示例如下:
"key=name; expires=Thu, 25 Feb 2016 04:18:00 GMT; domain=ppsc.sankuai.com; path=/; secure; HttpOnly"
expires
expires
選項用來設定“cookie 什麼時間內有效”。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
瀏覽器會清空。如果沒有設定該選項,則預設有效期為session
,即會話cookie
。這種cookie
在瀏覽器關閉後就沒有了。
expires
是 http/1.0協議中的選項,在新的http/1.1協議中expires
已經由max-age
選項代替,兩者的作用都是限制cookie 的有效時間。expires
的值是一個時間點(cookie失效時刻= expires
),而max-age
的值是一個以秒
為單位時間段(cookie失效時刻= 建立時刻+ max-age
)。
domain 和 path
domain
選項用來設定cookie
該發到哪個域名,path
選項用來設定cookie
該發往哪個路徑。如某個 cookie
設定為domain=ppsc.sankuai.com;
path=/pub;
,表示:若請求的地址域名是“ppsc.sankuai.com
”,路徑是“/pub
”或“/pub
下的任一子目錄”如/pub/example
、/pub/example/doc
時,瀏覽器才會將這個cookie
自動新增到請求頭部中。比如請求地址為http://ppsc.sankuai.com/pub
時,該cookie
會被髮送,但請求地址為http://ppsc.sankuai.com/
時,該cookie
不會被髮送。
如某個 cookie
設定為domain=sankuai.com;
path=/;
,表示:若請求的地址域名是“sankuai.com
”或其子域如“ppsc.sankuai.com
”、“dx.ppsc.sankuai.com
”等,路徑是“/
”或其下的任一子目錄如/pub/example
、/pub/example/doc
時,瀏覽器才會將這個cookie
自動新增到請求頭部中。
所以domain
和path
2個選項共同決定了cookie
何時被瀏覽器自動新增到請求頭部中傳送出去。如果沒有設定這兩個選項,則會使用預設值。domain
的預設值為設定該cookie
的網頁所在的域名,path
預設值為設定該cookie
的網頁所在的目錄。
secure
secure
選項用來設定cookie
只在確保安全的請求中才會發送。當請求是HTTPS
或者其他安全協議時,包含 secure
選項的 cookie
才能被髮送至伺服器。
預設情況下,cookie
不會帶secure
選項(即為空)。所以預設情況下,不管是HTTPS
協議還是HTTP
協議的請求,cookie
都會被髮送至服務端。但要注意一點,secure
選項只是限定了在安全情況下才可以傳輸給服務端,但並不代表你不能看到這個
cookie。
下面我們設定一個 secure
型別的 cookie:
document.cookie = "name=huang; secure";
之後你就能在控制檯中看到這個 cookie 了,如下圖所示:
這裡有個坑需要注意下:
如果想在客戶端即網頁中通過 js 去設定secure
型別的 cookie,必須保證網頁是https
協議的。在http
協議的網頁中是無法設定secure
型別cookie的。
httpOnly
這個選項用來設定cookie
是否能通過 js
去訪問。預設情況下,cookie
不會帶httpOnly
選項(即為空),所以預設情況下,客戶端是可以通過js
程式碼去訪問(包括讀取、修改、刪除等)這個cookie
的。當cookie
帶httpOnly
選項時,客戶端則無法通過js
程式碼去訪問(包括讀取、修改、刪除等)這個cookie
。
在客戶端是不能通過js
程式碼去設定一個httpOnly
型別的cookie
的,這種型別的cookie
只能通過服務端來設定。
那我們在頁面中怎麼知道哪些cookie
是httpOnly
型別的呢?看下圖:
凡是httpOnly
型別的cookie
,其
HTTP 一列都會打上√,如上圖中的PA_VTIME
。你通過document.cookie
是不能獲取的,也不能修改PA_VTIME
的。
——
httpOnly
與安全從上面介紹中,大家是否會有這樣的疑問:為什麼我們要限制客戶端去訪問
cookie
?其實這樣做是為了保障安全。試想:如果任何 cookie 都能被客戶端通過
document.cookie
獲取會發生什麼可怕的事情。當我們的網頁遭受了 XSS 攻擊,有一段惡意的script
指令碼插到了網頁中。這段script
指令碼做的事情是:通過document.cookie
讀取了使用者身份驗證相關的 cookie,並將這些 cookie 傳送到了攻擊者的伺服器。攻擊者輕而易舉就拿到了使用者身份驗證資訊,於是就可以搖搖大擺地冒充此使用者訪問你的伺服器了(因為攻擊者有合法的使用者身份驗證資訊,所以會通過你伺服器的驗證)。
如何設定 cookie?
知道了cookie
的格式,cookie
的屬性選項,接下來我們就可以設定cookie
了。首先得明確一點:cookie
既可以由服務端來設定,也可以由客戶端來設定。
服務端設定 cookie
不管你是請求一個資原始檔(如 html/js/css/圖片),還是傳送一個ajax
請求,服務端都會返回response
。而response
header
中有一項叫set-cookie
,是服務端專門用來設定cookie
的。如下圖所示,服務端返回的response
header
中有5個set-cookie
欄位,每個欄位對應一個cookie
(注意不能將多個cookie
放在一個set-cookie
欄位中),set-cookie
欄位的值就是普通的字串,每個cookie
還設定了相關屬性選項。
注意:
-
一個
set-Cookie
欄位只能設定一個cookie
,當你要想設定多個 cookie,需要新增同樣多的set-Cookie
欄位。 -
服務端可以設定cookie 的所有選項:
expires
、domain
、path
、secure
、HttpOnly
客戶端設定 cookie
在網頁即客戶端中我們也可以通過js
程式碼來設定cookie
。如我當前開啟的網址為http://dxw.st.sankuai.com/mp/
,在控制檯中我們執行了下面程式碼:
document.cookie = "name=Jonh; ";
檢視瀏覽器 cookie 面板如下圖所示,cookie
確實設定成功了,而且屬性選項 domain
、path
、expires
都用了預設值。
再執行下面程式碼:
document.cookie="age=12; expires=Thu, 26 Feb 2116 11:50:25 GMT; domain=sankuai.com; path=/";
檢視瀏覽器cookie 面板,如下圖所示,新的cookie
設定成功了,而且屬性選項 domain
、path
、expires
都變成了設定的值。
注意:
-
客戶端可以設定cookie 的下列選項:
expires
、domain
、path
、secure
(有條件:只有在https
協議的網頁中,客戶端設定secure
型別的 cookie 才能成功),但無法設定HttpOnly
選項。
用 js 如何設定多個 cookie
當要設定多個cookie
時, js 程式碼很自然地我們會這麼寫:
document.cookie = "name=Jonh; age=12; class=111";
但你會發現這樣寫只是添加了第一個cookie
“name=John”,後面的所有cookie
都沒有新增成功。所以最簡單的設定