NodeJS 使用 cookie 和 session

在這裏插入圖片描述


閱讀原文


前言

因爲瀏覽器無狀態的特性,cookie 技術應運而生,cookie 是一個會話級的存儲,大小 4KB 左右,用於瀏覽器將服務器設置的信息從新帶給服務器進行驗證,不支持跨域,在瀏覽器清空緩存或超過有效期後失效,不能存放敏感信息,session 是專門用於存儲最初設置給瀏覽器 cookie 數據的地方,咱們本篇就來討論一下 cookiesession 在 NodeJS 中的使用方式。數據庫


cookie 的基本使用

一、NodeJS 原生操做 cookie

下面是 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=truecookie,則會增長一條同名 cookie,原來的 cookie 不會被修改;
  • Expires 用來設置過時時間,絕對時間,值爲一個 GMTUTC 格式的時間;
  • max-age 一樣用來設置過時時間,相對時間,值爲一個正整數,單位 s

cookie 默認不支持存儲中文,若是存儲中文需先使用 encodeURIComponent 方法進行轉譯,將轉譯後的結果存入 cookie,在瀏覽器獲取 cookie 需使用 decodeURIComponent 方法轉回中文。瀏覽器

二、Koa 中操做 cookie

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 屬性上,分別爲 getset服務器

cookies.get 的參數爲獲取 cookie 的鍵名,返回值爲鍵對應的值,cookies.set 的第一個參數一樣爲 cookie 的鍵名,第二個參數爲鍵對應的值,第三個參數爲一個對象,用來配置該條 cookie 的規則,如 domainpath 和過時時間等,這裏 maxAge 值爲毫秒數。cookie

注意:Koa 中設置的 cookie 默認不容許瀏覽器端經過 document.cookie 獲取,可是服務器也能夠被欺騙,好比使用 postman 發送一個帶 Cookie 請求頭的請求,服務器能夠經過設置簽名來預防,即添加 signed 選項並將值設置爲 truesession

三、Koa 操做 cookie 方法的原理

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 的基本使用

一、NodeJS 原生使用 session

正常 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 中使用 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);

使用 Koakoa-session 之後,再也不須要咱們建立 session 對象進行存儲,而且 cookie-session 中間件幫咱們封裝了 API 能夠直接操做 mongoMySQL 數據庫,上面代碼中與用原生相比還增長了 cookiesession 的簽名和過時時間,比原生寫起來要方便不少。


總結

本篇內容更偏向於 cookiesession 在 NodeJS 中的使用,沒有過多的敘述理論性的內容,cookiesession 是相互依存的,也就是說共同使用的,如今已經有 JWT 的方案來替代,由於相比較下有不少優勢,但某些項目和特殊場景還在使用 cookiesession,因此仍是寫了這一篇,若是對 JWT 感興趣能夠看 經過一個案例理解 JWT

相關文章
相關標籤/搜索