nodeJS之Cookie和Session(一)node
一:Cookie
HTTP是一個無狀態協議,客戶端每次發出請求時候,下一次請求得不到上一次請求的數據,那麼如何將上一次請求和下一次請求的數據關聯起來呢?
好比登陸官網後,再切換到其餘頁面時候,那麼其餘的頁面是如何知道該用戶已經登陸了呢?因此這就可使用到cookie中的值來判斷了。express
cookie它是一個由瀏覽器和服務器共同協做實現的協議的。那麼cookie分爲以下幾步實現:
1. 服務器端向客戶端發送cookie。
2. 瀏覽器將cookie保存。
3. 以後每次請求都會將cookie發向服務器端。npm
1.1 服務器端發送cookie瀏覽器
服務器發送cookie給客戶端是經過HTTP響應報文實現的。在set-Cookie中設置給客戶端發送的cookie,cookie格式以下:
Set-Cookie: name=value; Max-Age=60; Path=/; domain=.domain.com;Expires=Sun, 27 May 2018 05:44:24 GMT; HttpOnly, secure;安全
以下圖所示服務器
其中name=value是必選項,其餘都是可選的,cookie主要構成以下:cookie
name: 一個惟一肯定cookie的名稱。
value: 存儲在cookie中字符串的值。
domain: cookie對於那個域下是有效的,
path: 表示這個cookie影響到的路徑,瀏覽器會根據這個配置,向指定的域中匹配的路徑發送cookie。
expires: 失效時間,表示cookie什麼時候失效的時間,若是不設置這個時間,瀏覽器就會在頁面關閉時將刪除全部的cookie,不過咱們也能夠本身設置過時時間。
注意:若是客戶端和服務器端設置的時間不一致,使用expires就會存在誤差。
max-age: 用來告訴瀏覽器此cookie多久過時(單位是秒),通常的狀況下,max-age的優先級高於expires。
HttpOnly: 告訴瀏覽器不容許經過腳本document.cookie去更改值,這個值在document.cookie中也是不可見的,可是在http請求會攜帶這個cookie,
注意:這個值雖然在腳本中使不可取的,可是在瀏覽器安裝目錄中是以文件形式存在的,這個設置通常在服務器端設置的。
secure:安全標誌,指定後,當secure爲true時候,在HTTP中是無效的,在HTTPS中才有效,表示建立的cookie只能在HTTPS鏈接中被瀏覽器傳遞到服務器端進行會話驗證,若是是HTTP鏈接則不會傳遞該信息,因此通常不會被且聽到。session
服務器端設置cookie代碼以下:app
const express = require('express'); const app = express(); app.listen(3001, () => { console.log('port listen 3001'); }); app.get('/', (req, res) => { res.setHeader('status', '200 OK'); res.setHeader('Set-Cookie', 'isVisit=1;domain=/;path=/;max-age=60*1000'); res.send('歡迎你~'); });
直接設置Set-Cookie過於原始,咱們能夠對cookie的設置過程作以下封裝;dom
const express = require('express'); const app = express(); app.listen(3001, () => { console.log('port listen 3001'); }); const serilize = function(name, value, options) { if (!name) { throw new Errpr('cookie must have name'); } var rets = []; value = (value !== null && value !== undefined) ? value.toString() : ''; options = options || {}; rets.push(encodeURIComponent(name) + "=" +encodeURIComponent(value)); if (options.domain) { rets.push('domain=' + options.domain); } if (options.path) { rets.push('path=' + options.path); } if (options.expires) { rets.push('expires=' + options.expires.toGMTString()); } if (options.maxAge && typeof options.maxAge === 'number') { rets.push('max-age=' + options.maxAge); } if (options.httpOnly) { rets.push('HTTPOnly'); } if (options.secure) { rets.push('secure'); } return rets.join(';'); }; app.get('/', (req, res) => { res.setHeader('status', '200 OK'); res.setHeader('Set-Cookie', serilize('isVisit', '1')); res.send('歡迎你~'); });
1.2 服務器端解析cookie
cookie能夠設置不一樣的域和路徑,因此對於同一個name value,在不一樣的域不一樣的路徑下是能夠重複的,瀏覽器會按照與當前請求的url或頁面地址最佳匹配的順序來排定前後順序。
服務器端解析代碼以下:
const parse = function(str) { if (!str) { return; } const dec = decodeURIComponent; var cookies = {}; const rets = str.split(/\s*;\s*/g); rets.forEach((r) => { const pos = r.indexOf('='); const name = pos > -1 ? dec(r.substr(0, pos)) : r; const val = pos > -1 ? dec(r.substr(pos + 1)) : null; // 只須要拿到最匹配的那個 if (!cookies.hasOwnProperty(name)) { cookies[name] = val; } }); return cookies; };
所以整個代碼以下:
const express = require('express'); const app = express(); app.listen(3001, () => { console.log('port listen 3001'); }); const serilize = function(name, value, options) { if (!name) { throw new Errpr('cookie must have name'); } var rets = []; value = (value !== null && value !== undefined) ? value.toString() : ''; options = options || {}; rets.push(encodeURIComponent(name) + "=" +encodeURIComponent(value)); if (options.domain) { rets.push('domain=' + options.domain); } if (options.path) { rets.push('path=' + options.path); } if (options.expires) { rets.push('expires=' + options.expires.toGMTString()); } if (options.maxAge && typeof options.maxAge === 'number') { rets.push('max-age=' + options.maxAge); } if (options.httpOnly) { rets.push('HTTPOnly'); } if (options.secure) { rets.push('secure'); } return rets.join(';'); }; const parse = function(str) { if (!str) { return; } const dec = decodeURIComponent; var cookies = {}; const rets = str.split(/\s*;\s*/g); rets.forEach((r) => { const pos = r.indexOf('='); const name = pos > -1 ? dec(r.substr(0, pos)) : r; const val = pos > -1 ? dec(r.substr(pos + 1)) : null; // 只須要拿到最匹配的那個 if (!cookies.hasOwnProperty(name)) { cookies[name] = val; } }); return cookies; }; app.get('/', (req, res) => { res.setHeader('status', '200 OK'); res.setHeader('Set-Cookie', serilize('isVisit', '1')); res.send('歡迎你~'); console.log(parse('isVisit=1')); });
在命令行中運行後能夠看到打印出cookie信息鍵值對,以下:
1.3 express中的cookie
express4中操做的cookie使用 cookie-parser模塊;以下代碼使用:
const express = require('express'); const cookieParser = require('cookie-parser'); const app = express(); app.listen(3000, () => { console.log('port listen 3000'); }); app.use(cookieParser()); app.get('/', (req, res) => { if (req.cookies.isVisit) { console.log(req.cookies); res.send('再次歡迎你'); } else { // cookie設置過時時間爲10分鐘 res.cookie('isVisit', 1, {maxAge: 60*1000}); res.send('歡迎你~'); } });
二:session
cookie操做很方便,可是使用cookie安全性不高,cookie中的全部數據在客戶端就能夠被修改,數據很容易被僞造;因此一些重要的數據就不能放在cookie當中了,而且cookie還有一個缺點就是不能存放太多的數據,爲了解決這些問題,session就產生了,session中的數據保留在服務端的。
2.1 基於Cookie來實現用戶和數據的映射
把數據放到cookie中是不可取的,可是咱們能夠將口令放在cookie中的,好比cookie中常見的會放入一個sessionId,該sessionId會與服務器端之間會產生
映射關係,若是sessionId被篡改的話,那麼它就不會與服務器端數據之間產生映射,所以安全性就更好,而且session的有效期通常比較短,通常都是設置是
20分鐘,若是在20分鐘內客戶端與服務端沒有產生交互,服務端就會將數據刪除。
session的原理是經過一個sessionid來進行的,sessionid是放在客戶端的cookie中,當請求到來時候,服務端會檢查cookie中保存的sessionid是否有,
而且與服務端的session data映射起來,進行數據的保存和修改,也就是說當咱們瀏覽一個網頁時候,服務端會隨機生成一個1024比特長的字符串,而後存在
cookie中的sessionid字段中,當咱們下次訪問時,cookie會帶有sessionid這個字段。
express 中 express-session模塊
在express中操做session可使用 express-session這個模塊,主要方法是session(options),options中包含可選的參數有:
name: 保存session的字段名稱。默認爲 connect.sid
store: session的存儲方式,默認存放在內存中。
secret: 經過設置secret字符串,來計算hash值並放在cookie中,使產生的signedCookie防篡改。
cookie: 設置存放session id的cookie的相關選項,默認爲 (default: { path: '/', httpOnly: true, secure: false, maxAge: null })
genid: 產生一個新的 session_id 時,所使用的函數, 默認使用 uid2 這個 npm 包。
rolling: 每一個請求都從新設置一個 cookie,默認爲 false。
resave: 即便 session 沒有被修改,也保存 session 值,默認爲 true
2.2 在內存中存儲session
以下代碼:
const express = require('express'); const session = require('express-session'); const app = express(); app.listen(3002, () => { console.log('port listen 3002'); }); app.use(session({ secret: 'somesecrettoken', cookie: { maxAge: 1*60*1000 } // 1分鐘 })); app.get('/', (req, res) => { /* 檢查session中的isVisit字段 */ if (req.session.isVisit) { res.send('再次歡迎你'); } else { req.session.isVisit = true; res.send('歡迎你第一次來~'); } });