因爲瀏覽器無狀態的特性,cookie
技術應運而生,cookie
是一個會話級的存儲,大小 4KB
左右,用於瀏覽器將服務器設置的信息從新帶給服務器進行驗證,不支持跨域,在瀏覽器清空緩存或超過有效期後失效,不能存放敏感信息,session
是專門用於存儲最初設置給瀏覽器 cookie
數據的地方,咱們本篇就來討論一下 cookie
和 session
在 NodeJS 中的使用方式。數據庫
下面是 cookie
在 Node 原生中的讀取和寫入方法。跨域
// 原生中操做 cookie const http = require("http"); // 建立服務 http.createServer((req, res) => { if (req.url === "/read") { // 讀取 cookie console.log(req.headers.cookie); res.end(req.headers.cookie); } else if (req.url === "/write") { // 設置 cookie res.setHeader("Set-Cookie", [ "name=panda; domain=panda.com; path=/write; httpOnly=true", `age=28; Expires=${new Date(Date.now() + 1000 * 10).toGMTString()}`, `address=${encodeURIComponent("回龍觀")}; max-age=10` ]); res.end("Write ok"); } else { res.end("Not Found"); } }).listen(3000);
上面代碼建立了一個 http
服務器,能夠經過讀取 cookie
請求頭的值來獲取瀏覽器發來的 cookie
,服務器能夠經過給瀏覽器設置響應頭 Set-Cookie
實現對瀏覽器 cookie
的設置,多個 cookie
參數爲數組,在數組內能夠規定每一條 cookie
的規則,中間使用一個分號和一個空格隔開。數組
domain
用來設置容許訪問 cookie
的域;path
用來設置容許訪問 cookie
的路徑;httpOnly
用來設置是否容許瀏覽器中修改 cookie
,若是經過瀏覽器修改設置過 httpOnly=true
的 cookie
,則會增長一條同名 cookie
,原來的 cookie
不會被修改;Expires
用來設置過時時間,絕對時間,值爲一個 GMT
或 UTC
格式的時間;max-age
一樣用來設置過時時間,相對時間,值爲一個正整數,單位 s
。cookie
默認不支持存儲中文,若是存儲中文需先使用 encodeURIComponent
方法進行轉譯,將轉譯後的結果存入 cookie
,在瀏覽器獲取 cookie
需使用 decodeURIComponent
方法轉回中文。瀏覽器
Koa
是當下流行的 NodeJS 框架,是對原生 Node 的一個輕量的封裝,可是內部實現了快捷操做 cookie
的方法,下面是原生中對 cookie
的操做在 Koa
中的寫法。緩存
// Koa 中操做 cookie const Koa = require("koa"); const Router = require("koa-router"); // 建立服務和路由 const app = new Koa(); const router = new Router(); // 簽名須要設置 key app.keys = ["shen"]; router.get("/read", (ctx, next) => { // 獲取 cookie let name = ctx.cookies.get(name) || "No name"; let name = ctx.cookies.get(age) || "No age"; ctx.body = `${name}-${age}`; }); router.get("/write", (ctx, next) => { // 設置 cookie ctx.cookies.set("name", "panda", { domain: "panda.com" }); ctx.cookies.set("age", 28, { maxAge: 10 * 1000, signed: true }); }); // 使用路由 app.use(router.routes()); app.listen(3000);
在 Koa
中將獲取和設置 cookie
的方法都掛在了 ctx
上下文對象的 cookies
屬性上,分別爲 get
和 set
。服務器
cookies.get
的參數爲獲取 cookie
的鍵名,返回值爲鍵對應的值,cookies.set
的第一個參數一樣爲 cookie
的鍵名,第二個參數爲鍵對應的值,第三個參數爲一個對象,用來配置該條 cookie
的規則,如 domain
、path
和過時時間等,這裏 maxAge
值爲毫秒數。cookie
注意:Koa
中設置的 cookie
默認不容許瀏覽器端經過 document.cookie
獲取,可是服務器也能夠被欺騙,好比使用 postman
發送一個帶 Cookie
請求頭的請求,服務器能夠經過設置簽名來預防,即添加 signed
選項並將值設置爲 true
。session
cookies
對象都是掛在 ctx
上來實現的,使用過 Koa
都知道若是要操做 ctx
就會用到中間件的思想,咱們這就看看這兩個方法使用原生封裝的過程。app
// Koa 中 ctx.cookies 對象 get 和 set 方法的原理 const Koa = require("koa"); const querystring = require("querystring"); const app = new Koa(); app.use(async (ctx, next) => { // 獲取 cookie const get = key => { let cookies = ctx.get("cookie") || ""; return querystring.parse(result, "; ")[key]; }; // 設置 cookie,存儲全部的 cookie,等於 setHeader 中的第二個參數 let cookies = []; const set = (key, val, options = {}) => { // 用於構造單條 cookie 和權限等設置的數組,默認存放這條 cookie 的鍵和值 let single = [`${key}=${encodeURIComponent(val)}`]; // 下面是配置 if (options.domain) { arr.push(`domain=${options.domain}`); } if (options.maxAge) { arr.push(`Max-Age=${options.maxAge}`); } if (options.path) { arr.push(`path=${options.path}`); } if (options.httpOnly) { arr.push(`HttpOnly=true`); } // 將配置組合到 single 中後轉爲字符串存入 cookies cookies.push(single.join("; ")); // 設置給瀏覽器 ctx.set("Set-Cookie", cookies); } // 將獲取和設置 cookie 的方法掛在 cookies 對象上 ctx.cookies = { get, set }; await next(); });
在 get
方法內部獲取 cookie
請求頭的值並根據傳入的 key
獲取值,set
方法內,將傳入的鍵值和選項拼接成符合 cookie
的字符串,經過 Set-Cookie
響應頭設置給瀏覽器。框架
正常 session
是存放在數據庫中的,咱們這裏爲了方便就用一個名爲 session
的對象來代替。
// 原生中使用 session const http = require("http"); const uuid = require('uuid/v1'); // 生成隨字符串 const querystring = require("querystring"); // 存放 session const session = {}; // 建立服務 http.createServer((req, res) => { if (req.url === "/user") { // 取出 cookie 存儲的用戶 ID let userId = querystring.parse(req.headers["cookie"], "; ")["study"]; if (userId) { if (session[userId].studyCount === 0) res.end("您的學習次數已用完"); session[userId].studyCount--; } else { // 生成 userId userId = uuid(); // 將用戶信息存入 session session[userId] = { studyCount: 30 }; // 設置 cookie req.setHeader("Set-Cookie", [`study=${userId}`]); } // 響應信息 res.end(` 您的用戶 ID 爲 ${userId}, 剩餘學習次數爲:${session[userId].studyCount} `); } else { res.end("Not Found"); } }).listen(3000);
上面寫的案例是一個網校的場景,一個新用戶默認有 30
次學習機會,之後每次訪問服務器學習次數減 1
,若是 studyCount
值爲 0
,則提示學習次數用完,不然提示當前用戶的 ID
和剩餘學習次數,session
中存儲的是每個用戶 ID
對應的剩餘學習次數,這樣就不會輕易的被修改學習剩餘次數,由於服務器只認用戶 ID
,再經過 ID
去更改對應的剩餘次數(固然忽略了別人冒充這個 ID
的狀況,只能減,不能加),這樣就不會由於篡改 cookie
而篡改用戶存在 session
中的數據,除非連整個數據庫都拖走。
咱們接下來使用 Koa
實現和上面一摸同樣的場景,在 Koa
的社區中提供了專門操做 session
的中間件 koa-session
,使用前需安裝。
// Koa 中使用 session const Koa = require("koa"); const Router = require("koa-router"); const session = requier("koa-session"); const uuid = require("uuid/v1"); // 建立服務和路由 const app = new Koa(); const router = new Router(); // cookie 的簽名 app.keys = ["panda"]; // 使用 koa-session 中間件 app.use(session({ key: "shen", maxAge: 10 * 1000 }, app)); router.get("/user", (ctx, next) => { // 取出 cookie 存儲的用戶 ID let userId = ctx.cookie("study"); if (ctx.session.userId) { if (ctx.session[userId].studyCount === 0) res.end("您的學習次數已用完"); ctx.session[userId].studyCount--; } else { // 生成 userId userId = uuid(); // 將用戶信息存入 session ctx.session[userId] = { studyCount: 30 }; // 設置 cookie ctx.cookies.set("study", userId); } // 響應信息 ctx.body = ` 您的用戶 ID 爲 ${userId}, 剩餘學習次數爲:${session[userId].studyCount} `; }); // 使用路由 app.use(router.routes()); app.listen(3000);
使用 Koa
的 koa-session
之後,再也不須要咱們建立 session
對象進行存儲,而且 cookie-session
中間件幫咱們封裝了 API 能夠直接操做 mongo
和 MySQL
數據庫,上面代碼中與用原生相比還增長了 cookie
和 session
的簽名和過時時間,比原生寫起來要方便不少。
本篇內容更偏向於 cookie
和 session
在 NodeJS 中的使用,沒有過多的敘述理論性的內容,cookie
和 session
是相互依存的,也就是說共同使用的,如今已經有 JWT 的方案來替代,由於相比較下有不少優勢,但某些項目和特殊場景還在使用 cookie
和 session
,因此仍是寫了這一篇,若是對 JWT 感興趣能夠看 經過一個案例理解 JWT。