「使用passport middleware實作登入系統」相關筆記
2021-06-24 13:41 Express
總結
本次練習包含以下幾個主要功能:
- 使用passport來驗證使用者輸入的登入資訊(帳號、密碼)是否有效
- 並
passport.authenticate()會接手處理驗證成功/失敗的 redirect endpoint - 搭配
req.isAuthenticated()設計 middleware 來控制登入前後可視之畫面(例:登入前不可查看/dashboard 頁面、登入後無法查看/user/login 頁面)
- 並
- 使用connect-flash搭配 express-handlebars 的 partial 來實現
res.redirect()後顯示錯誤訊息的功能
環境
passport: 0.4.1
passport-local: 1.0.0
express: 4.17.1
express-session: 1.17.2
connect-flash: 0.1.1
os: Windows_NT 10.0.18363 win32 x64
筆記
app.js
- 13 行開始:參考passport 的官方示範,將
resave與saveUninitialized兩個參數都設定為true- 根據express-session 的官方文件:
resave: Forces the session to be saved back to the session store, even if the session was never modified during the request.saveUninitialized: Forces a session that is “uninitialized” to be saved to the store. A session is uninitialized when it is new but not modified.
- 21 行開始
res.locals.successMessage = req.flash('successMessage'): Every view will have access to any error or success messages that you flash. Ref.: How to send flash messages in Express 4.0?res.locals: An object that contains response local variables scoped to the request, and therefore available only to the view(s) rendered during that request / response cycle (if any). Otherwise, this property is identical toapp.locals. Ref.: Express: res.locals- 須注意
req.flash('error')此名稱為 passport 專用,將 error 修改為其他名稱後,passportdone()的錯誤訊息就無法顯示
config/passport.js
- 說明:
function loginVerify(passport)匯出後由app.jsrequire 後使用,參考app.js原始碼 33-34 行 - 第 6 行的
{ usernameField: 'email' }- By default, LocalStrategy expects to find credentials in parameters named
usernameandpassword. If your site prefers to name these fields differently, options are available to change the defaults. - 登入表單中的 username 與 password name 欄位若不是
name="username"與name="password"的話,可額外指定要讀取的表格欄位,{ usernameField: 'email' }的意思即是使用登入表單中的name="email"欄位作為usernameField
- By default, LocalStrategy expects to find credentials in parameters named
- 第 9 行的
{ message: 'User not exist' }:其中'User not exist'會由req.flash('error')顯示 done(null, user)與done(null, false)done(null, user): If the credentials are valid, the verify callback invokesdone()to supply Passport with the user that authenticated.done(null, false): If the credentials are not valid,done()should be invoked with false instead of a user to indicate an authentication failure.
serializeUser與deserializeUser- 16 行:Only the
user.idis serialized to the session, keeping the amount of data stored within the session small. - 20 行:When subsequent requests are received, this ID (
user.id) is used to find the user, which will be restored toreq.user. - Ref.: Understanding passport serialize deserialize
Q: Where does
user.idgo afterpassport.serializeUserhas been called? A: Theuser.idis saved in the session, and is later used to retrieve the whole object via thedeserializeUserfunction.serializeUserdetermines which data of the user object should be stored in the session.
- 16 行:Only the
config/auto.js
-
關於
.isAuthenticated()
- How is req.isAuthenticated() in Passport JS implemented? For any request, you can check if a user is authenticated or not by using this method.
- No Mention of isAuthenticated() in docs #683
There is no bug… The thing is that the
isAuthenticated()andisUnauthenticated()functions are not mentioned anywhere in the docs.
-
在
routes/home.js與routes/user.js中作為 middleware 使用isLoggedIn:若使用者已登入,則可繼續前往該 endpoint;反之若使用者未登入的話,則導向/user/login,並顯示提示訊息(Please log in to view this page.)notLoggedIn:若使用者在已經登入的情況下前往/user/login或/user/register,則自動導回/dashboard
routes/modules
- home.js
- 第 12 行
res.render('dashboard', { user: req.user.username }):通過登入驗證的使用者資料會被儲存在req.user中,要在dashboard模板中顯示username的話,從req.user中取username的值即可
- 第 12 行
- user.js
- 21-25 行:直接由
passport.authenticate('local', { ... })接管登入驗證的程序,驗證成功的話導向/dashboard,失敗則導回/user/login,並因為failureFlash設定為true,login模板會根據 config/passport.js 中設定的條件顯示相對應的錯誤訊息({ message: 'User not exist' }與{ message: 'Password incorrect' }) - Setting the
failureFlashoption totrueinstructs Passport to flash an error message using the message given by the strategy’s verify callback, if any. 需注意 views/partials 中的{{#if error}}不可換成其他名稱,req.flash('error')中的'error'也不可換(換了就無法顯示訊息) req.logout(): Passport exposes alogout()function onreqthat can be called from any route handler which needs to terminate a login session. Invokinglogout()will remove thereq.userproperty and clear the login session (if any).
- 21-25 行:直接由
views/partials
- 使用 partials 時的 views 資料夾結構:
/views
/layouts
main.handlebars
/partials
messages.handlebars
dashboard.handlebars
index.handlebars
login.handlebars
register.handlebars
- 使用方式:設定好 partials 內容後,將
{{> 檔案名稱}}(例:{{> messages}})插回其餘模板即可 - 會自動根據
{{#if 參數是否為true}}來決定顯示哪些 partials 內容,舉例:若 passport.js 中的done()回傳 error 的話,partials 檔案messages.handlebars中的 24-29 行即會顯示在login或register模板中