1. 程式人生 > 程式設計 >Node 中 Cookie、Session 與 Redis 快取

Node 中 Cookie、Session 與 Redis 快取

HTTP 協議和 TCP/IP 協議組中其它協議相同,用於客戶端和伺服器端之間的通訊,HTTP是一種無狀態協議,協議本身不儲存客戶端和服務端的通訊狀態,也就是說在 HTTP 這個級別不會對請求或響應做持久化處理,當然這也是為了更快的處理大量事務,確保協議的可伸縮性。

一、cookie

1.1 概述

在 HTTP 協議中,制定了 Cookie 機制,用於實現客戶端和伺服器之間的狀態共享。

Cookie 是解決 HTTP 無狀態性的有效手段,伺服器可以設定或讀取 cookie 中所包含的資訊。常見的情景是:

  • 當使用者登入後,伺服器會傳送包含登入憑據的 cookie 到使用者瀏覽器客戶端,而瀏覽器對該 cookie 進行某種形式的儲存(記憶體或硬碟)。
  • 使用者再次訪問該網站時,瀏覽器會傳送該 cookie(cookie 未到期時)到伺服器,伺服器對該憑據進行驗證,合法時使使用者不必輸入使用者名稱和密碼就可以直接登入。

為什麼瀏覽器會這樣操作?

當網頁要發 http 請求時,瀏覽器會先檢查是否有相應的 cookie,有則自動新增在 request header 中的Cookie欄位中。這是瀏覽器自動做的,而且每一次 http 請求瀏覽器都會這樣做。這個特點很重要,因為這關係到什麼樣的資料適合儲存在 cookie 中。如果資料並不是每個請求都需要發給服務端的資料,瀏覽器這樣處理無疑增加了網路開銷;而如果資料是每個請求都需要發給服務端的(比如身份認證資訊),瀏覽器這樣自動處理就大大免去了重複新增操作。所以對於設定“每次請求都要攜帶的資訊(最典型的就是身份認證資訊)”就特別適合放在cookie中,其他型別的資料就不適合了。

但在 localStorage 出現之前,cookie 被濫用當做了儲存工具。什麼資料都放在 cookie 中,即使這些資料只在頁面中使用而不需要隨請求傳送到服務端。當然 cookie 標準還是做了一些限制的:每個域名下的 cookie 的大小最大為4KB,每個域名下的 cookie 數量最多為20個(但很多瀏覽器廠商在具體實現時支援大於20個)。

img

1.3 cookie 類別

cookie 總是儲存在客戶端,通常可以按 expires 到期時間分為兩類:記憶體式cookie、硬碟式cookie

記憶體式:儲存在記憶體中,瀏覽器關閉後清除,也叫非持久儲存(會話 cookie)。cookie 不包含到期日期,則可視為會話 cookie。 會話 cookie 儲存在記憶體中,當瀏覽器關閉時,cookie 將永久丟失。

硬碟式:儲存在硬碟中,瀏覽器關閉後不會清除,除非手動清除或到了過期時間,也叫持久儲存(持久 cookie)。cookie 包含到期日期,則可視為永續性 cookie。 在指定的到期日期,cookie 將從磁碟中刪除。

1.4 HTTP 協議中為 cookie 服務的首部欄位

Set-cookie: response headers 欄位。屬性之間用分號和空格隔開。cookie的值字串可以用encodeURIComponent()來保證它不包含任何逗號、分號或空格( cookie 值中禁止使用這些值).

Cookie: request header 欄位,服務端接收到的cookie資訊。

img

屬性 說明
{KEY}={VALUE} cookie key/value (必須)(*可以多個嗎?*可以,需要寫多個 set-cookie語句)
expires={DATE} 指定瀏覽器可以傳送 cookie 的有效期(預設為session,即會話 cookie,瀏覽器關閉為止)
domain={DOMAIN} 指定 cookie 可以送達的主機名。(預設值為當前檔案訪問地址中的主機部分(但是不包含子域名))
path={PATH} 指定一個 URL 路徑,這個路徑必須出現在要請求的資源的路徑中才可以傳送 Cookie 首部。字元%x2F ("/") 可以解釋為檔案目錄分隔符,此目錄的下級目錄也滿足匹配的條件(例如,如果 path=/docs,那麼 "/docs","/docs/Web/" 或者 "/docs/Web/HTTP" 都滿足匹配的條件)。(預設為當前檔案的路徑)
secure 僅在 https 通訊時才會傳送 cookie
HttpOnly 禁止 cookie 被 js 指令碼訪問(js 獲取 cookie 的方法是document.cookie),防止跨站監本攻擊對 cookie 資訊竊取

expires是 http/1.0協議中的選項,在 http/1.1 協議中expires已經由max-age 選項代替,兩者的作用都是限制 cookie 的有效時間。expires的值是一個時間點(cookie失效時刻),而max-age 的值是一個以秒為單位時間段。如果兩者都存在,max-age優先順序更高。

另外,max-age的預設值是 -1(即有效期為 session );max-age有三種可能值:負數、0、正數。負數表示有效期為 session;0表示刪除cookie;正數表示有效期為建立時刻 + max-age

1.5 中介軟體

cookie-parser (看檔案學習如何與 Express 或者 Koa 結合使用)

也可以參考這裡:chenshenhai.github.io/koa2-note/n…

二、session

session 需要藉助 cookie 實現,session 資料儲存在服務端,而只在 cookie 中儲存一個 sessionId,可以保證安全性和降低伺服器負載。

可以查詢中介軟體 express-session 或者 koa-session 的檔案。

也可以參考這裡:chenshenhai.github.io/koa2-note/n…

三、Redis

Redis 是一個開源的、使用 ASCI C 編寫的、可基於記憶體及持久化的日誌型 key-value 資料庫,提供多種語言 API。可用作資料庫、快取記憶體和訊息佇列代理,非常適合於短時間內高頻訪問但又不需要長期訪問的簡單資料儲存。

session存在的問題:session 用於在服務端儲存使用者會話狀態(如:使用者登入資訊等) ,session 在程式重啟、多程式執行、負載均衡、跨域等情況時,會出現丟失或多程式、多個負載站點間狀態不能共享的情況。

要解決這些問題:我們需要將 session 持久化儲存,Redis 儲存是一個非常不錯的 session 持久化解決方案。

Redis 是一個高效能的 key-value 資料庫(與 SQL 資料庫的關係是什麼?如何儲存?

3.1 概述

特點

  • Redis 支援資料的持久化,可以將記憶體中的資料儲存在磁碟中,重啟的時候可以再次載入進行使用。
  • Redis 不僅僅支援簡單的 key-value 型別的資料,同時還提供listsetzset(有序集合)、hash等資料結構的儲存
  • Redis支援資料的備份,即 master-slave 模式的資料備份(?)

優勢

  • 效能極高 – Redis 能讀的速度是110000次/s,寫的速度是81000次/s(什麼版本?什麼硬體?來源?
  • 豐富的資料型別 – Redis 支援二進位制案例的 Strings,Lists,Hashes,Sets 及 Ordered Sets 資料型別操作
  • 原子 – Redis 的所有操作都是原子性的,同時 Redis 還支援對幾個操作全並後的原子性執行
  • 豐富的特性 – Redis還支援 publish/subscribe,通知,key 過期等等特性

3.2 中介軟體

node-redis connect_redis

示例:

const Koa = require('koa');
const app = new Koa();
const session = require('koa-session');
const redis = require('redis'); //引入 node redis 庫
const client = redis.createClient(6379,'127.0.0.1'); //連線本地Redis服務
const { promisify } = require('util');
//用 promisify改造 client.hgetall
const hgetallAsync = promisify(client. hgetall).bind(client);
app.keys= ['some secret hurr'];
const store = { //配置 Redis 如何存取 Session 
    get: async (key,maxAge) => await hgetallAsync(key),//從Redis獲取Session
    set: (key,sess,maxAge) => client.hmset(key,session),destroy: key => client.hdel(key),};
const config = {
    key: 'koa:sess',maxAge: 86400000,overwrite: true,httpOnly: true,signed: true,store,};
app.use(session(config,app));
app.use(ctx => {
    if (ctx.path === '/favicon.ico') return;
    let n = ctx.session.views || 0;
    ctx.session.views = ++n;
    ctx.body = n + `views`
})
app.listen(3000);
複製程式碼

參考

  1. node中Session持久化與Redis快取
  2. 聊一聊 cookie
  3. 許可權處理 - 用redis實現分散式session~ (cookie && session )
  4. developer.mozilla.org/zh-CN/docs/…