Express 是在 nodejs 下著名的 web framework,
由於可透過 middleware 的設計, 掛載各式各樣的 pluging 來完成各項功能,
因此可以在很多 framework 中看到
而 Passport 則是一個 Express middleware, 可以提供 web 權限驗證的功能,
除了基本的 username/password 外, 也可以使用 OAuth 進行第三方登入驗證
做完基本介紹, 這邊簡單說明如何使用 Passport 完成基本的使用者驗證功能
首先當然要先在我們的 Express 專案中安裝 Passport,
而 Passport 對於不同的驗證方式, 需要載入相對應的 Strategies,
這邊先採用 LocalStrategy 也就是常見的 username/password 驗證,
另外要保持使用者的登入狀態, 就需要 session 機制, 所以也需要預先安裝 express-session
,
npm i --save passport passport-local express-session
再來就是需要一些使用者的資料囉!
基本上應該要存放在 DB 上來管理, 不過這邊我們是要先實作驗證機制,
就直接在 app.js
程式中放些使用者的資料!
app.js1 2 3 4 5 6 7 8 9 10
| var Users = { test1:{ name: 'test1', password: 'password1' }, test2:{ name: 'test2', password: 'password2' } };
|
接著在程式中引用 passport
及 passport-local
並且在 Express routing 設定前將 passport middleware 載入
app.js1 2 3 4 5 6 7 8 9
| var passport = require('passport') , LocalStrategy = require('passport-local').Strategy; app.use(require('express-session')({ secret: 'keyboard cat', resave: false, saveUninitialized: false })); app.use(passport.initialize()); app.use(passport.session());
|
這邊有幾點要注意的
express-session
必須在 passport.session()
使用前先載入
express-session
的 option 中有個 secre
是必要選項,
主要是將存在使用者端的 session cookie, 計算 hash 後存放,
這邊使用的是官網的範例, 建議正式發布需要進行修改, 以免被竄改
passport.initialize()
務必在 express 設定 routing 前使用,
否則登入驗證程式無法正常運作
接著將加入驗證檢查的程式
app.js1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| passport.use(new LocalStrategy( function (username, password, done) { user = Users[username]; if (user === null) { return done(null, false, { message: 'Invalid user' }); } if (user.password !== password) { return done(null, false, { message: 'Invalid password' }); } return done(null, user); } )); passport.serializeUser(function (user, done) { done(null, user.name); }); passport.deserializeUser(function (username, done) { done(null, Users[username]); });
|
LocalStrategy
的 callback function 中, username 及 password 欄位預設對應 login form 的帳號/密碼欄位名稱,
如果不想使用預設欄位名稱則可以參考以下用法
1 2 3 4 5 6 7 8
| passport.use(new LocalStrategy({ usernameField: 'email', passwordField: 'passwd' }, function(username, password, done) { } ));
|
另外 serializeUser
是將使用者資訊存在 session 中,
一般是使用 user ID 以便在資料庫中查詢, 這邊則是用 username
而 deserializeUser
則是讓 express 的 req.user
可以取得目前的使用者詳細資料
由於 Express 預設的 view engine 是 jade, 接著就在 views 資料中加入 login.jade
login.jade1 2 3 4 5 6 7 8 9 10 11 12
| extends layout block content form(action="/login", method="post") div label Username: input(type="text", name="username") div label Password: input(type="password", name="password") div input(type="submit", value="Log In")
|
接著繼續在 app.js 中加入登入頁面的 routing 設定, 當然也別忘了登出功能
app.js1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| app.get('/login', function(req, res, next) { res.render('login'); }); app.post('/login', passport.authenticate('local', { successRedirect: '/', failureRedirect: '/login' }) ); app.get('/logout', function (req, res) { req.logout(); res.redirect('/'); });
|
到這邊基本的使用者驗證功能就完成了,
而我們要如何在頁面中取得使用者的資訊呢?
還記得前面所說的, 可以從 req.user
可以取得目前的使用者詳細資料,
例如我們要在 index 頁面中顯示使用者資訊, 就可以如下面的程式傳遞到 view 頁面中
1 2 3
| app.get('/', function(req, res, next) { res.render('index', { title: 'Express', user: req.user }); });
|
但是想要在每個頁面都知道登入狀態及使用者資訊, 這麼做就很麻煩,
這時候就可以使用 express middleware 的方式, 讓每個頁面載入前呼叫這段程式
app.js1 2 3 4 5
| app.use(function (req, res, next) { res.locals.user = req.user; res.locals.isAuthenticated = req.isAuthenticated(); next(); });
|
這樣每個 view 頁面就可以加入下面的程式,
或是放到 partial 的頁面, 這樣整個功能就完成了!
1 2 3 4 5
| if(isAuthenticated) p Hello #{user.name}! a(href="/logout") 登出 else a(href="/login") 登入
|
至於後續的修改有時間再繼續寫吧!
參考資料
Express API reference
Passport Document
範例程式下載