今天,來到鐵人賽第二十五天。
下午了,所以,不多言了。
我們來看看今天的進度吧。
在上一篇了解了 cookie 如何在 express 中應用, 並且, 實作了登入。
現在,我們來看看 session版,會有什麼不同。
session 在 express 使用, 必須 使用 第三方 模組,我們去express-session 參考其說明與使用方式。
session主要的使用方式是 session(option)
,有幾個必須知道的事:
Note: session data 並不會儲存在 cookie 裡,cookie 存的是 sessionID.
Note: session data 儲存在 server端,
接下來,官方網站有寫:意思應該是,直接用這個套件,你不用再用cookie-parser了。這個,就留給有時間再來試驗了。
Note: 從 version1.5.0版以後, 它可以不用再被 cookie-parser 綁約了…
這個套件, 可以直接讀寫 cookie 在req及res。
如果你使用express-session 又使用cookie-parser 如session裡設定的secret 不同於 cookie 的簽章,可能會導致錯誤發生。
◎ 接著,我們看看有哪些重要的option
:
name:在response中,設定的 sessionID cookie 名字。預設是 connect.sid
proxy:當你有設置secure cookie,信任的反向proxy
resave:強制將session存回 session store, 即使它沒有被修改。預設是 true
rolling:強制在每一次回應時,重新設置一個sessionID cookie。到期, 將重置為原始 maxAge, 重新計數時效性。
saveUninitialized:強制將未初始化的session存回 session store,未初始化的意思是它是新的而且未被修改。
store:session在server 端的存放方式,預設 MemoryStore。
cookie:設定sessionID 的cookie相關選項。其選項與我們在cookie介紹一樣。
預設是 { path: ‘/’, httpOnly: true, secure: false, maxAge: null }.
secret(必要選項)
:用來簽章 sessionID 的cookie, 可以是一secret字串或是多個secret組成的一個陣列。如果是陣列, 只有第一個元素會被 簽到 sessionID cookie裡。而在驗證請求中的簽名時,才會考慮所有元素。
因為 session 是儲存在server端的,所以,我們可以為 Session 設置存放位置。
一般而言,session 可以存放在:
1.記憶體内存
2.cookie本身
3.redis 或 memcached 等缓存中(常見)
4.數據庫中 ex. mongoDB。
這邊,我將示範,在mongoDB 存 session,很簡單,只要3個步驟:
首先,必須先安裝 express-session 及 connect-mongo (還有更多session儲存的API, 可以參考 https://github.com/expressjs/session#compatible-session-stores)
安裝好後,我們可以用簡單的 計數器程式 做例子!首先,引入module:
var session = require('express-session');
const MongoStore = require('connect-mongo')(session);
store:new MongoStore({url:’mongodb://localhost:27017/sessiondb’})
如此,大功告成!!
啟動你的 express server ….
接著, 我們可以進入mongo shell 看到,初始化資料庫已經被自動新增,並且,自動幫我們新增sessions collection:
當我們網頁重新整理,session被建立了,即刻存到 sessions 去,所以,我們再find() 有資料!!
然而,此sessionID 的生存時間只有1分鐘, 時間一到, 會自動失效, 幫我們清除舊的。
所以, 我們第二次find()時,在sessions collection 已經沒有找到!!
我們直接應用上一篇的登入例子,這邊有些不同的設置地方,再加以說明!
Step 1. 首先,安裝並載入相關檔案 express
, express-session
, body-parser
, connect-mongo
Step 2. 一樣的建立 Login.html 放入 /public/session/ 之下。
而我們這裡與上一篇的 cookie登入實作,共用同一樣版,所以,這部份不需變動。
要注意: Login.html 的action 記得指向 /session/post
seindex.js
var express = require('express');
// 引入 express-session
var session = require('express-session');
// session store
const MongoStore = require('connect-mongo')(session);
var bodyParser = require('body-parser');
var routerSession=require('./routes/seloginAPI');
var app = express();
//set view engine
app.set("view engine","jade")
//set view directory
app.set("views",__dirname+"/views")
//將request進來的data 轉成 json()
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
// Create a router to handle routes for a set of seloginAPI
// 抓出來, 變成獨立檔案
// -------------------------------------------------------
//static file like .js, .json, .xml, html....
app.use(express.static(__dirname+'/public'));
// Apply this router on (/cookie)
app.use('/session', routerSession);
app.listen(3000,function(){
console.log('Ready...for 3000');
});
seindex.js
//設置session相關設定
app.use(session({
secret: 'recommand 128 bytes random string',
store:new MongoStore({url:'mongodb://localhost:27017/sessiondb'}),
resave: false,
saveUninitialized: true,
cookie: { maxAge: 600 * 1000 } //10分鐘到期
}));
因為我們有設置 store 所以,我們多設定了 resave 及 saveUninitialized 選項。
/rotues/seloginAPI.js
。接下來,我們將分別說明這三件事:
/rotues/seloginAPI.js
// 表單送出後...
seloginAPI.post('/post', function(req, res) {
// ...
if(req.body.firstName=="" || req.body.lastName=="")
{
return res.redirect('Login.html');
}else if(req.body.firstName==req.session.firstName
&& req.body.lastName==req.session.lastName)
//如果輸入的,在session store已有儲存..
{
req.session.time++; //同一連線的登入次數, 就加 1
return res.redirect('/session'); //就直接導向到...
}
else
{
//session store裡沒有的,就會重新設置
req.session.firstName=req.body.firstName;
req.session.lastName=req.body.lastName;
req.session.time=1;
return res.redirect('/session');
}
});
這段程式說明:表單送出後,開始進行條件判斷。
如果 fristName, lastName 其中一個欄位沒有填寫,則再重回登入頁。
否則,再進行下一個條件判斷:只要,兩者輸入,在session store 已有 (在未登出情況下,同一筆連線),表示 session 早已存在,登入次數加1,直接到需要驗證的頁面即可!–(這裡的判別,可以做省略,主要是為了測試store用!)
否則,重新設置session,並導向驗證頁面。
/rotues/seloginAPI.js
var isLogin=false;
// 進入需要驗證的頁面...
seloginAPI.get('/',function(req,res){
// ...
var name='guest';
isLogin=false;
var Logtime=1;
if(req.session.firstName && req.session.lastName){
name=req.session.firstName+ ' '+req.session.lastName;
isLogin = true;
Logtime= req.session.time;
}
res.render('index', { title: 'Express', member:name, logstatus:isLogin,time:Logtime });
});
這段程式說明:
一開始預設,所有的登入狀態 isLogin 都是false,預設的登入者是guest。
如果,我們接收到的 session 皆存在,則改變 登入者姓名 及 登入狀態。
然而,無論有沒有登入,皆會導到 index.jade 樣版,去做呈現。
這邊與cookie的不同, 多了一個測試 進入次數的計數呈現(非必要)
/rotues/seloginAPI.js
// 登出...
seloginAPI.get('/logout', function(req, res) {
req.session.destroy();
return res.redirect('/session');
});
這段程式說明:
登出時, 所有session清空!爾後, 導向 /session 路由,
因為有這一步,所以,只要上述的測試,只要它有登出,即使在同一連線,進入次數都是1。
(因為,session store 也只是一個暫存我們session的地方,暫時的~)
最後,完成的Demo,畫面像這樣:
我們進入mongo shell 看, 確實 王小明在這session 生存時間 10分鐘內,
同一連線下未登出狀態, 登入了3次
完整程式檔案可於這裡下載:
https://github.com/circleuniv/login
最後,放上一張,小編在釐清session運作機制的手寫圖(獻醜了)。