1. 程式人生 > >【其他學習】聊一聊 cookie,做大專案必懂的知識點

【其他學習】聊一聊 cookie,做大專案必懂的知識點

咱們不搞一開始就一大堆理論知識介紹,怕把人講懵了...... 咱們換一個思維方式——"從現象看本質",先說說我們看到了什麼,再從看到的現象中提出問題,最後深入尋找答案。

我們看到的 cookie

我自己建立了一個網站,網址為http://ppsc.sankuai.com。在這個網頁中我設定了幾個cookieJSSESSIONIDPA_VTIMEskmtutctest

在 chrome 瀏覽器中開啟這個網站,進入開發者模式,點選Resources欄 -> 選擇cookies,我們會看到如下圖所示的介面:

解釋一下:左邊欄Cookies下方會列舉當前網頁中設定過cookie

的域都有哪些。上圖中只有一個域,即“ppsc.sankuai.com”。而右側區域顯示的就是某個域下具體的 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

中提取那4個cookie

上面兩張圖展示了cookie的基本通訊流程:設定cookie => cookie被自動新增到request header中 => 服務端接收到cookie。這個流程中有幾個問題需要好好研究:

  1. 什麼樣的資料適合放在cookie中?

  2. cookie是怎麼設定的?

  3. cookie為什麼會自動加到request header中?

  4. 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選項包括:expiresdomainpathsecureHttpOnly。在設定任一個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自動新增到請求頭部中。

所以domainpath2個選項共同決定了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的。當cookiehttpOnly選項時,客戶端則無法通過js程式碼去訪問(包括讀取、修改、刪除等)這個cookie

在客戶端是不能通過js程式碼去設定一個httpOnly型別的cookie的,這種型別的cookie只能通過服務端來設定。

那我們在頁面中怎麼知道哪些cookiehttpOnly型別的呢?看下圖:

凡是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 的所有選項:expiresdomainpathsecureHttpOnly

客戶端設定 cookie

在網頁即客戶端中我們也可以通過js程式碼來設定cookie。如我當前開啟的網址為http://dxw.st.sankuai.com/mp/,在控制檯中我們執行了下面程式碼:

document.cookie = "name=Jonh; ";

檢視瀏覽器 cookie 面板如下圖所示,cookie確實設定成功了,而且屬性選項 domainpathexpires都用了預設值。

再執行下面程式碼:

document.cookie="age=12; expires=Thu, 26 Feb 2116 11:50:25 GMT; domain=sankuai.com; path=/";

檢視瀏覽器cookie 面板,如下圖所示,新的cookie設定成功了,而且屬性選項 domainpathexpires都變成了設定的值。

注意:

  • 客戶端可以設定cookie 的下列選項:expiresdomainpathsecure(有條件:只有在https協議的網頁中,客戶端設定secure型別的 cookie 才能成功),但無法設定HttpOnly選項。

用 js 如何設定多個 cookie

當要設定多個cookie時, js 程式碼很自然地我們會這麼寫:

document.cookie = "name=Jonh; age=12; class=111";

但你會發現這樣寫只是添加了第一個cookie“name=John”,後面的所有cookie都沒有新增成功。所以最簡單的設定