超越 Cookie:當今的客戶端資料儲存技術

瘋狂的技術宅發表於2019-08-06

超越 Cookie:當今的客戶端資料儲存

作者:Adam Giese

翻譯:瘋狂的技術宅

原文:https://blog.logrocket.com/be...

未經允許嚴禁轉載

當 cookie 被首次引入時,它是瀏覽器儲存資料的唯一方式。之後又有了很多新的選擇:Web Storage API、IndexedDB 和 Cache API。那麼 cookie 死了嗎?我們來看看這些在瀏覽器中儲存資料的技術。

Cookies

Cookie 是由伺服器傳送或在客戶端上設定的資訊單位,儲存在使用者的本地瀏覽器上。它們會自動附加到每個請求上。由於 HTTP 是無狀態協議,因此 cookie 允許將資訊儲存在客戶端上,以便將其他上下文資料傳給該伺服器。

Cookie 有一些標誌,對於提高資料的安全性非常有用。 HttpOnly 標誌阻止用 JavaScript 訪問 cookie 的行為,只有附加在 HTTP 請求上時才能訪問它們。這非常適合防止通過 XSS(跨站點指令碼)攻擊造成資料洩露。

此外,Secure 標誌確保僅在通過 HTTPS 協議傳送請求時才傳送 cookie。 SameSite 標誌,可以設定為 laxstrict(它們的差異看這裡),可用於幫助防止 CSRF(跨站點請求偽造)請求。它告訴瀏覽器只有在請求是與請求者在同一域中的 URL 時才傳送 cookie。

什麼時候使用 cookies?

那麼,在哪些情況下你希望獲得 Cookie?最常見的應用場景之一是授權 token 。由於 HttpOnly 標誌為 XSS 攻擊新增了額外的保護層,SameSite 可以防止 CSRF,而 Secure 可以確保你的 cookie 被加密,這使你的身份驗證token 有額外的保護層。

由於 auth token 非常小,因此你無需擔心請求過大。此外由於它們會自動附加到每個請求,因此使用 cookie 可以在伺服器上確定使用者是否經過身份驗證。這對於伺服器呈現的內容非常有用,例如你希望將未經過身份驗證的使用者重定向到登入頁面。

Cookie 的另一個用途是儲存使用者的語言程式碼。由於你可能希望在大多數請求中訪問使用者的語言,因此你可以利用它自動附加。

如何使用 cookies?

前面經討論了要使用 cookie 的原因,現在來看看你可以如何使用 cookie。要從伺服器上給客戶端設定 cookie,需要在 HTTP 響應中新增 Set-Cookie 標頭。 Cookie 應採用 key=value 的格式。如果你要在 Node.js 程式中設定 cookie,你的程式碼可能像下面這樣:

response.setHeader('Set-Cookie', ['user_lang=en-us', 'user_theme=dark_mode']);

這將會設定兩個 cookie:它將 user_lang 設定為 en-us,將 user_theme 設定為 dark_mode

Cookie 也可以由客戶端操縱。要設定 cookie,可以用 key=value 的格式為 document.cookie 賦值。如果 key 已存在,則會被覆蓋掉。

document.cookie = 'user_lang=es-es';

如果已經定義了 user_lang,它現在等於es-es

你可以通過訪問 document.cookie 值來檢視所有的 cookie。這將返回一串以分號做分隔的鍵值對。

document.cookie = 'user_lang=en-us';
document.cookie = 'user_theme=light_mode';
console.log(document.cookie); // 'user_lang=en-us; user_theme=light_mode;'

要增加鍵值對的可訪問性,可以使用以下函式將此字串解析為物件:

const parseCookies = x => x
  .split(';')
  .map(e => e.trim().split('='))
  .reduce((obj, [key, value]) => ({...obj, [key]: value}), {});

If you need to set one of the flags onto your cookie, you can add them after a semicolon. For example, if you’d like to set the Secure and SameSite flags onto your cookie, you would do the following:

如果你需要將其中一個標誌設定到 cookie 上,可以在分號後新增它們。例如你想在 Cookie 上設定 SecureSameSite 標誌,則可以執行以下操作:

document.cookie = 'product_ids=123,321;secure;samesite=lax'

由於 HTTPOnly 的作用是使 cookie 只能在伺服器上訪問,因此它只能由伺服器新增。

除了這些安全標誌之外,你還可以設定 Max-Age( cookie 應該儲存的秒數)或 Expires(Cookie應該過期的日期)。如果這些都未設定,則 cookie 將跟隨瀏覽器會話的持續時間。如果使用者使用隱身模式,則會在使用者會話關閉時刪除 Cookie。

由於處理 cookie 的介面不是很友好,所以你可以使用諸如 js-cookie 之類的庫來方便對其的操作。

Web Storage API

Web Storage API 是一種在本地儲存資料的新選項。它在 HTML5 中中新增,Web Storage API 包括localStoragesessionStorage。雖然 cookie 通常處理 server/client 通訊,但 Web Storage API 最適用於儲存客戶端資料。

我們已經將 cookie 作為在本地儲存資料的選項,為什麼還需要 Web 儲存?其中一個原因是:由於 cookie 會自動新增到每個 HTTP 請求中,因此請求大小會變得臃腫。所以你可以用 Web Storage API 儲存比 cookie 更大量的資料。

另一個優點是更直觀的 API。如果使用 cookie,你需要手動解析 cookie 字串來訪問各個鍵。 Web Storage 使這更加容易。如果要設定或獲取值,可以使用 setItemgetItem

localStorage.setItem('selected_tab', 'FAQ');
localSTorage.getItem('selected_tab'); // 'FAQ'

鍵和值都必須是字串。如果你想儲存一個物件或陣列,可以在儲存時呼叫 JSON.stringify() 並在讀取時呼叫 JSON.parse() 來實現。

const product = {
  id: '123',
  name: 'Coffee Beans',
};

localStorage.setItem('cached_product', JSON.stringify(product));
JSON.parse(localStorage.getItem('cached_product'));

local storage 的另一個用例是在多個選項卡之間同步資料。通過為 'storage' 事件新增偵聽器,你可以在另一個選項卡或視窗中更新資料。

window.addEventListener('storage', () => {
  console.log('local storage has been updated');
});

僅當在另一個文件中修改本地或會話儲存時才會觸發此事件。也就是說,你無法在當前瀏覽器選項卡中偵聽 storage 的更改。不幸的是,截至撰寫本文時,儲存事件監聽器尚未在 Chrome 上得到支援

那麼localStoragesessionStorage 之間有什麼區別呢?與 cookie 不同,Web Storage API 沒有過期或最大期限功能。如果使用 localStorage,除非手動刪除,否則資料將無限期保留。你可以通過執行 localStorage.removeItem('key') 來刪除單個鍵的值,或者通過執行 localStorage.clear() 清除所有資料。

如果使用 sessionStorage,則資料將僅持續到當前會話結束。如果你沒有設定最大時間或過期,它將被視為與 cookie 保持的方式相似。在任何一種情況下,如果使用者使用隱身,本地儲存都不會在會話之間保留資料。

IndexedDB

如果 cookie 和 localStorage 都不符合你的要求,還有另一種選擇:IndexedDB,一個瀏覽器內建的資料庫系統。

localStorage 同步執行所有方法時,IndexedDB 會非同步呼叫它們。這將會允許訪問資料而不會阻塞其餘程式碼。當你處理大量可能訪問代價高昂的程式碼時,這非常有用。

IndexedDB 在其儲存的資料型別方面也具有更大的靈活性。雖然 cookies 和 localStorage 僅限於儲存字串,但 IndexedDB 可以儲存可以通過“結構化克隆演算法”複製的任何型別的資料。這包括 ObjectDateFileBlobRegEx 以及更多型別

效能和靈活性增加的缺點是 IndexedDB 的 API 更低階且更復雜。幸運的是有許多庫可以解決這個問題。

localForage 為 IndexedDB 提供了一個更簡單的類似 localStorage 的 API。 PouchDB 提供了一個可以離線的儲存 API,可以與線上 CouchDB 資料庫同步。 idb 是一個小型庫,具有更簡單的基於 promise 的 API。 Dexie 新增了更強大的查詢 API,同時保持了良好的效能。根據你的使用情況還有許多選擇。

Cache API

另一種用於持久資料的專用工具是 Cache API。雖然它最初是為 service workers 建立的,但它可用於快取任何網路請求。 Cache API 公開了 Window.caches,它提供了儲存和檢索響應的方法,允許你儲存可永遠以後訪問的 RequestsResponses 對。

例如,如果你想在從 API 請求響應之前檢查瀏覽器的快取以獲取響應,則可以執行以下操作:

const apiRequest = new Request('https://www.example.com/items');
caches.open('exampleCache') // opens the cache
  .then(cache => {
    cache.match(apiRequest) // checks if the request is cached
      .then(cachedResponse => 
        cachedResponse || // return cachedReponse if available
        fetch(apiRequest) // otherwise, make new request
          .then(response => {
            cache.put(apiRequest, response); // cache the response
            return response;
          })
        })
    .then(res => console.log(res))
})

第一次執行程式碼時,它將快取響應。隨後每次都會快取請求,並且不會發出網路請求。

總結

在瀏覽器上儲存資料的每種方法都有其自己的用途。如果資訊很小,很敏感,並且可能在伺服器上使用,那麼 cookie 就是最佳選擇。如果要儲存更大且更不敏感的資料,Web Storage API 可能是更好的選擇。

如果你打算儲存大量結構化資料,IndexedDB 非常棒。 Cache API 用於儲存來自 HTTP 請求的響應。根據你的需要,有很多工具可供使用。

其他資源和擴充套件閱讀

你可以通過閱讀 MDN 文件來獲取更多資訊:


本文首發微信公眾號:前端先鋒

歡迎掃描二維碼關注公眾號,每天都給你推送新鮮的前端技術文章

歡迎掃描二維碼關注公眾號,每天都給你推送新鮮的前端技術文章

歡迎繼續閱讀本專欄其它高贊文章:


相關文章