我們不搞一開始就一大堆理論知識介紹,怕把人講懵了...... 我們換一個思惟方式——"從現象看本質",先說說咱們看到了什麼,再從看到的現象中提出問題,最後深刻尋找答案。javascript
我本身建立了一個網站,網址爲http://ppsc.sankuai.com
。在這個網頁中我設置了幾個cookie
:JSSESSIONID
,PA_VTIME
,skmtutc
,test
。css
在 chrome 瀏覽器中打開這個網站,進入開發者模式,點擊Resources
欄 -> 選擇cookies
,咱們會看到以下圖所示的界面:html
解釋一下:左邊欄Cookies
下方會列舉當前網頁中設置過cookie
的域都有哪些。上圖中只有一個域,即「ppsc.sankuai.com」。而右側區域顯示的就是某個域下具體的 cookie
列表,對應上圖就是「ppsc.sankuai.com」域下設置的4個cookie
。前端
在這個網頁中我往http://ppsc.sankuai.com/getList
接口發了一個 Ajax 請求,request header
以下圖所示:java
從上圖中咱們會看到request header
中自動添加了Cookie
字段(我並無手動添加這個字段哦~),Cookie
字段的值其實就是我設置的那4個 cookie
。這個請求最終會發送到http://ppsc.sankuai.com
這個服務器上,這個服務器就能從接收到的request header
中提取那4個cookie
。web
上面兩張圖展現了cookie
的基本通訊流程:設置cookie
=> cookie
被自動添加到request header
中 => 服務端接收到cookie
。這個流程中有幾個問題須要好好研究:ajax
什麼樣的數據適合放在cookie
中?chrome
cookie
是怎麼設置的?segmentfault
cookie
爲何會自動加到request header
中?api
cookie
怎麼增刪查改?
咱們要帶着這幾個問題繼續往下閱讀。
首先必須明確一點,存儲cookie
是瀏覽器提供的功能。cookie
實際上是存儲在瀏覽器中的純文本,瀏覽器的安裝目錄下會專門有一個 cookie 文件夾來存放各個域下設置的cookie
。
當網頁要發http
請求時,瀏覽器會先檢查是否有相應的cookie
,有則自動添加在request header
中的cookie
字段中。這些是瀏覽器自動幫咱們作的,並且每一次http
請求瀏覽器都會自動幫咱們作。這個特色很重要,由於這關係到「什麼樣的數據適合存儲在cookie
中」。
存儲在cookie
中的數據,每次都會被瀏覽器自動放在http
請求中,若是這些數據並非每一個請求都須要發給服務端的數據,瀏覽器這設置自動處理無疑增長了網絡開銷;但若是這些數據是每一個請求都須要發給服務端的數據(好比身份認證信息),瀏覽器這設置自動處理就大大免去了重複添加操做。因此對於那設置「每次請求都要攜帶的信息(最典型的就是身份認證信息)」就特別適合放在cookie
中,其餘類型的數據就不適合了。
但在 localStorage 出現以前,cookie
被濫用當作了存儲工具。什麼數據都放在cookie
中,即便這些數據只在頁面中使用而不須要隨請求傳送到服務端。固然cookie
標準仍是作了一些限制的:每一個域名下的cookie 的大小最大爲4KB,每一個域名下的cookie
數量最多爲20個(但不少瀏覽器廠商在具體實現時支持大於20個)。
JS 原生的 API提供了獲取cookie
的方法:document.cookie
(注意,這個方法只能獲取非 HttpOnly 類型的cookie
)。在 console 中執行這段代碼能夠看到結果以下圖:
打印出的結果是一個字符串類型,由於cookie
自己就是存儲在瀏覽器中的字符串。但這個字符串是有格式的,由鍵值對 key=value
構成,鍵值對之間由一個分號
和一個空格
隔開。
每一個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
選項用來設置「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
)。
另外,max-age
的默認值是-1
(即有效期爲session
);若max-age
有三種可能值:負數、0、正數。負數:有效期session
;0
:刪除cookie
;正數:有效期爲建立時刻+ max-age
domain
是域名,path
是路徑,二者加起來就構成了 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
和path
2個選項共同決定了cookie
什麼時候被瀏覽器自動添加到請求頭部中發送出去。若是沒有設置這兩個選項,則會使用默認值。domain
的默認值爲設置該cookie
的網頁所在的域名,path
默認值爲設置該cookie
的網頁所在的目錄。
特別說明1:
發生跨域xhr請求時,即便請求URL的域名和路徑都知足 cookie 的 domain 和 path,默認狀況下cookie也不會自動被添加到請求頭部中。若想知道緣由請閱讀本文最後一節)特別說明2:
domain是能夠設置爲頁面自己的域名(本域),或頁面自己域名的父域,但不能是公共後綴 public suffix。舉例說明下:若是頁面域名爲 www.baidu.com, domain能夠設置爲「www.baidu.com」,也能夠設置爲「baidu.com」,但不能設置爲「.com」或「com」。
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的。
這個選項用來設置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
既能夠由服務端來設置,也能夠由客戶端來設置。
無論你是請求一個資源文件(如 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
在網頁即客戶端中咱們也能夠經過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
選項。
當要設置多個cookie
時, js 代碼很天然地咱們會這麼寫:
document.cookie = "name=Jonh; age=12; class=111";複製代碼
但你會發現這樣寫只是添加了第一個cookie
「name=John」,後面的全部cookie
都沒有添加成功。因此最簡單的設置多個cookie
的方法就在重複執行document.cookie = "key=name"
,以下:
document.cookie = "name=Jonh";
document.cookie = "age=12";
document.cookie = "class=111";複製代碼
要想修改一個cookie
,只須要從新賦值就行,舊的值會被新的值覆蓋。但要注意一點,在設置新cookie時,path/domain
這幾個選項必定要舊cookie 保持同樣。不然不會修改舊值,而是添加了一個新的 cookie。
刪除一個cookie
也挺簡單,也是從新賦值,只要將這個新cookie的expires
選項設置爲一個過去的時間點就好了。但一樣要注意,path/domain/
這幾個選項必定要舊cookie 保持同樣。
cookie
實際上是個字符串,但這個字符串中逗號、分號、空格
被當作了特殊符號。因此當cookie的 key 和 value 中含有這3個特殊字符時,須要對其進行額外編碼,通常會用escape
進行編碼,讀取時用unescape
進行解碼;固然也能夠用encodeURIComponent/decodeURIComponent
或者encodeURI/decodeURI
(三者的區別能夠參考這篇文章)。
var key = escape("name;value");
var value = escape("this is a value contain , and ;");
document.cookie= key + "=" + value + "; expires=Thu, 26 Feb 2116 11:50:25 GMT; domain=sankuai.com; path=/";複製代碼
以前在介紹 XHR 的一篇文章裏面提過:默認狀況下,在發生跨域時,cookie 做爲一種 credential 信息是不會被傳送到服務端的。必需要進行額外設置才能夠。具體緣由和如何設置能夠參考個人這篇文章:你真的會使用XMLHttpRequest嗎?
另外,關於跨域資源共享 CORS
極力推薦你們閱讀阮一峯老師的這篇 跨域資源共享 CORS 詳解。
何時 cookie 會被覆蓋:name/domain/path 這3個字段都相同的時候;
若是顯式設置了 domain,則設置成什麼,瀏覽器就存成什麼;但若是沒有顯式設置,則瀏覽器會自動取 url 的 host 做爲 domain 值;
新的規範中,顯式設置 domain 時,若是 value 最前面帶點,則瀏覽器處理時會將這個點去掉,因此最後瀏覽器存的就是沒有點的(注意:但目前大多數瀏覽器並未所有這麼實現)
前面帶點‘.’和不帶點‘.’有啥區別:
帶點:任何 subdomain 均可以訪問,包括父 domain
不帶點:只有徹底同樣的域名才能訪問,subdomain 不能(但在 IE 下比較特殊,它支持 subdomain 訪問)
我們今天就聊到這裏,如有不對之處歡迎各位指正~~
最後附上一些參考資料: