天天看點

Node.js+Express 開發之Cookie、Session 使用詳解

 為什麼有cookie 和 session ?

  因為HTTP協定是沒有狀态的,當使用者再次通路網站時,沒法判斷之前是否登陸過,于是就有了cookies和session,用來儲存使用者的一些資訊。

  cookie 和 session 差別?

  cookie 是存放在用戶端浏覽器的,每個域名下通常限制為50個cookie,每個cookie 的值大小限制為4K。session 是存放在伺服器端的,可以存儲無限大的資料,但大量的session,會占用較多的伺服器記憶體。

  cookie 隻能存放字元串類型資料,session可以存放任意類型資料。

  cookie 是儲存在浏覽器用戶端的,是以也很容易被提取,安全方面存在隐患。session 儲存在伺服器端相對安全。

  cookie的工作原理

  當使用者登入網站成功,伺服器會在傳回響應資料的同時,将使用者的cookie資訊也下發到用戶端,之後用戶端每次發起請求會攜帶着這個cookie,進而免去網站登入的步驟。

  session的工作原理

  使用者的session資料儲存在伺服器記憶體中,伺服器隻下發一個session辨別 (session_id),它是一個唯一的随機字串,被儲存到cookie中,下次浏覽器的請求攜帶着包含session_id的cookie,然後伺服器在記憶體中查詢該session_id 是否有對應的資料,如果有則是已登入使用者。

  看起來 session 技術相當于Cookie技術的更新,session是基于cookie的,但是cookie隻是起到了 session_id 載體的作用,而url的查詢參數,http請求頭裡的字段都可以起到session_id 載體的作用,是以沒有cookie也可以實作session。另外,使用者的session資料不僅可以存放在伺服器記憶體中,也可以存放到檔案或資料庫中,要持久化session 同時也要持久化cookie的session_id。

  nodejs+express 開發中 cookie 的使用

  node.js 的web架構 express 在4.x版本後,許多子產品不再包含在其中,而是需要單獨下載下傳安裝。

  cookie 常用子產品是 cookie-parser,安裝指令如下:

npm install cookie-parser
           

  基本用法

  代碼示例:

let express = require("express");
let cookieParser = require("cookie-parser"); // 引用cookie-parser

let app = express();
app.use(cookieParser()); // 使用cookie中間件cookie-parser

// 首頁,讀取cookie
app.get("/", function (req, res) {
console.log("name: " + req.cookies.name, "profile: " + JSON.stringify(req.cookies.profile));
res.send("name: " + req.cookies.name + "<br/>" + "profile: " + JSON.stringify(req.cookies.profile));
});

// 登入,注冊cookie
app.get("/login", function (req, res) {

// 定義cookie參數對象
let cookieOptions = {
httpOnly: true,
maxAge: 10 * 60 * 1000, // 10分鐘後cookie失效
}

// 建立cookie,值為字元串
res.cookie("name", "xiaoyu", cookieOptions);

// 建立cookie,值可以寫成json對象形式 {....} 、[...]
res.cookie('profile', {
age: '25',
height: '160cm',
weight: '50kg'
}, cookieOptions);

res.send("已注冊cookie");
});

// 退出,清除cookie
app.get("/exit", function (req, res) {
res.clearCookie("name");
res.clearCookie("profile");
res.send("已清除cookie");
});

app.listen(3000);
           

  使用簽名cookie

  使用簽名 cookie,起防篡改功能。

  代碼示例:

let express = require("express");
let cookieParser = require("cookie-parser"); // 引用cookie-parser

let app = express();
app.use(cookieParser("1234567abcdefg")); // 使用cookie-parser,傳入簽名防篡改,可自定義一個複雜的字串

// 首頁,讀取cookie
app.get("/", function (req, res) {
console.log("name: " + req.signedCookies.name, "profile: " + JSON.stringify(req.signedCookies.profile));
res.send("name: " + req.signedCookies.name + "<br/>" + "profile: " + JSON.stringify(req.signedCookies.profile));
});

// 登入,注冊cookie
app.get("/login", function (req, res) {

// 定義cookie參數對象
let cookieOptions = {
httpOnly: true,
maxAge: 10 * 60 * 1000, // 10分鐘後cookie失效
signed: true // 使用簽名
}

// 建立cookie,值為字元串
res.cookie("name", "xiaoyu", cookieOptions);

// 建立cookie,值可以寫成json對象形式 {....} 、[...]
res.cookie('profile', {
age: '25',
height: '160cm',
weight: '50kg'
}, cookieOptions);

res.send("已注冊cookie");
});

// 退出,清除cookie
app.get("/exit", function (req, res) {
res.clearCookie("name");
res.clearCookie("profile");
res.send("已清除cookie");
});

app.listen(3000);
           

  cookie 參數選項說明:

   domain:表示cookie在什麼域名下有效,預設為目前網站域名。如果跨域通路,A站域名為 A.test.com,B站域名為 B.test.com,那麼在域A生産一個令域A和域B都能通路的cookie就要将該cookie的domain設定為: ".test.com"

  expires:cookie過期時間,類型為Date。例如:expires: new Date(Date.now() + 10*60*1000)  表示10分鐘後cookie過期。如果沒有設定或者設定為0,那麼該cookie 在關閉浏覽器後失效。expires早在http/1.0中已經定義,max-age在http/1.1中定義, 現在基本都是http/1.1,expires已經被max-age屬性所取代,建議優先使用maxAge

  maxAge:和expires類似,設定cookie過期的時間,指明從現在開始,多少毫秒以後cookie到期。maxAge:10*60*1000  表示10分鐘後Cookie過期。 完全不寫 maxAge 項,隻要關閉浏覽器cookie就失效。maxAge:0  表示從用戶端删除此cookie。  

  httpOnly:禁止JS腳本document.cookie 讀取cookie内容,防止XSS攻擊産生. 預設為false。建議設定為true

  path:cookie在什麼路徑下有效,預設為'/'

  secure:當secure值為true時,cookie在HTTP中是無效,隻在HTTPS中才有效。預設為false

  signed:使用簽名,起到防篡改功能。預設為false。  此項并不是加密,cookie值仍然被明文顯示出來,對于cookie的加密可自己使用md5、sha1、hmac、aes等方法,或使用中間件cookie-encrypter

  對 cookie 加密

  這裡使用第三方子產品 cookie-encrypter 對cookie 進行加密。該子產品預設采用的是AES256 對稱加密算法

     安裝cookie-encrypter

npm install cookie-encrypter
           

  代碼示例

let express = require("express");
let cookieParser = require("cookie-parser");
let cookieEncrypter = require("cookie-encrypter"); // 引用cookie-encrypter 子產品

let app = express();
const secretKey = 'foobarbaz1234567foobarbaz1234567'; // 預設cookie-encrypter子產品使用的是 AES256 加密算法,須輸入32個字元
app.use(cookieParser(secretKey));
app.use(cookieEncrypter(secretKey)); // 傳入密鑰,加密cookie,主要是這句

app.get("/", function (req, res) {
console.log("name: " + req.signedCookies.name, "profile: " + JSON.stringify(req.signedCookies.profile));
res.send("name: " + req.signedCookies.name + "<br/>" + "profile: " + JSON.stringify(req.signedCookies.profile));
});

app.get("/login", function (req, res) {
let cookieOptions = {
httpOnly: true,
signed: true,
maxAge: 10 * 60 * 1000,
// plain: true // 該選項是cookie-encrypter子產品的選項。等于true時,表示不再使用cookie-encrypter進行加密。
};
res.cookie('name', 'xiaoyu', cookieOptions);
res.cookie('profile', {
age: '25',
height: '160cm',
weight: '50kg'
}, cookieOptions);
res.send("已注冊cookie");
});

app.get("/exit", function (req, res) {
res.clearCookie('name');
res.clearCookie('profile');
res.send("已清除cookie");
});

app.listen(3000);
           

  nodejs+express 開發中 session 的使用

  處理session常用的中間件之一是 express-session

  安裝 express-session 子產品,指令如下:

npm install express-session
           

  基本用法

  代碼示例

let express = require("express");
let session = require("express-session");
let app = express();

app.use(session({
secret: "nodejs world",
resave: false,
saveUninitialized: false,
name: "session_id",
rolling: true,
cookie: {
path: '/',
httpOnly: true,
secure: false,
maxAge: 30 * 60 * 1000 // 設定session_id的cookie有效時間,也是背景session的有效時長。(預設為值為null,當關閉浏覽器時,session_id cookie失效)。
}
}));

app.get("/", function (req, res) {
if (req.session.login) {
res.send("歡迎您" + req.session.userName + ", " + req.sessionID);
} else {
res.send("你沒有登入!");
}
});

app.get("/login", function (req, res) {
req.session.login = true;
req.session.userName = "xiaoyu";
res.send("您已經成功登入");
});

app.get("/exit", function (req, res) {
req.session.destroy();
res.send("您已經退出");
});

app.listen(3000);
           

  session 配置選項說明

  secret: 對session_id cookie的簽名密鑰。

  resave: 強制儲存 session 即使它并沒有變化。預設為true,建議設定成false

  saveUninitialized: 強制将未初始化的session存儲。預設為 true。設定為true 不管用不用到session都會初始化。設定為false 用到session時才會去初始化。

  name: 這裡設定session_id 的cookie的名字。預設名為 'connect.sid'

  rolling: 在每次請求時強行設定cookie,這将重置session-id 的 cookie過期時間。預設:false

  cookie:  session_id 的cookie 選項。預設值是 { path: '/', httpOnly: true, secure: false, maxAge: null }

  将session 存放在 mongoDB 資料庫中

  session 預設存放在記憶體中,也可以存放到 redis,mongodb 等資料庫中。express 生态中都有相應子產品支援。

  将session存儲到mongodb 資料庫,可以安裝connect-mongo子產品支援

npm install connect-mongo
           

  代碼示例

let express = require("express");
let session = require("express-session");
let MongoStore = require('connect-mongo')(session); // 引用 connect-mongo

let app = express();

app.use(session({
secret: "nodejs world",
resave: false,
saveUninitialized: false,
name: "session_id",
rolling: true,
cookie: {
path: '/',
httpOnly: true,
secure: false,
maxAge: 30 * 60 * 1000 // session_id cookie 和 session的有效時長
},
store: new MongoStore({
     url: 'mongodb://localhost:27017/mydb', // 指定mongoDB 主機位址、端口、資料庫
     ttl: 30*60 // 設定session過期時間,時間到自動移除。機關秒。一般設定為30分鐘即可。未設定cookie:maxAge時有效。
})
}));

app.get("/", function (req, res) {
if (req.session.login) {
res.send("歡迎您" + req.session.userName + ", " + req.sessionID);
} else {
res.send("你沒有登入!");
}
});

app.get("/login", function (req, res) {
req.session.login = true;
req.session.userName = "xiaoyu";
res.send("您已經成功登入");
});


app.get("/exit", function (req, res) {
req.session.destroy();
res.send("您已經退出");
});

app.listen(3000);
           

  将session 存放在 redis 資料庫中  

  将session存儲到redis 資料庫,可以安裝redis-mongo子產品支援。事先需安裝好redis的nodejs用戶端

  • npm install redis

  • npm install connect-redis

  代碼示例

let express = require("express");
let session = require("express-session");
let redis = require("redis"); // 引用redis用戶端
let RedisStore = require('connect-redis')(session); // 引用connect-redis
let app = express();

let redisClient = redis.createClient({
    // 設定redis 主機位址、端口、資料庫、密碼
    host: "localhost",
    port: "6379",
    //password: "123456",
    db: 1
});

app.use(session({
    secret: "nodejs world",
    resave: false,
    saveUninitialized: false,
    name: "session_id",
    rolling: true,
    cookie: {
        path: '/',
        httpOnly: true,
        secure: false,
        maxAge: 30 * 60 * 1000 // session_id cookie 和 session的有效時長
    },
    store: new RedisStore({
        client: redisClient, // 指定redis用戶端
        ttl: 30 * 60, // 設定session過期時間,時間到自動移除。機關秒。一般設定為30分鐘即可。未設定cookie:maxAge時有效。
        logErrors: true, // 是否列印redis出錯資訊,預設false
        prefix: "sess:" // key字首,預設為 "sess:"
    })
}));

app.get("/", function (req, res) {
    if (req.session.login) {
        res.send("歡迎您" + req.session.userName + ", " + req.sessionID);
    } else {
        res.send("你沒有登入!");
    }
});

app.get("/login", function (req, res) {
    req.session.login = true;
    req.session.userName = "xiaoyu";
    res.send("您已經成功登入");
});

app.get("/exit", function (req, res) {
    req.session.destroy();
    res.send("您已經退出");
});

app.listen(3000);
           

Node.js+Express 開發之Cookie、Session 使用詳解

繼續閱讀