普通文組 2.5

「使用passport middleware實作登入系統」相關筆記

總結

本次練習包含以下幾個主要功能:

  • 使用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 的官方示範,將resavesaveUninitialized兩個參數都設定為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 to app.locals. Ref.: Express: res.locals
    • 須注意req.flash('error')此名稱為 passport 專用,將 error 修改為其他名稱後,passport done()的錯誤訊息就無法顯示

config/passport.js

  • 說明:function loginVerify(passport)匯出後由app.js require 後使用,參考app.js原始碼 33-34 行
  • 第 6 行的{ usernameField: 'email' }
    • By default, LocalStrategy expects to find credentials in parameters named username and password. 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
  • 第 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 invokes done() 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.
  • serializeUserdeserializeUser
    • 16 行:Only the user.id is 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 to req.user.
    • Ref.: Understanding passport serialize deserialize Q: Where does user.id go after passport.serializeUser has been called? A: The user.id is saved in the session, and is later used to retrieve the whole object via the deserializeUser function. serializeUser determines which data of the user object should be stored in the session.

config/auto.js

  • 關於.isAuthenticated()

    We just don't have it in the docs

  • routes/home.jsroutes/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的值即可
  • user.js
    • 21-25 行:直接由passport.authenticate('local', { ... })接管登入驗證的程序,驗證成功的話導向/dashboard,失敗則導回/user/login,並因為failureFlash設定為truelogin模板會根據 config/passport.js 中設定的條件顯示相對應的錯誤訊息({ message: 'User not exist' }{ message: 'Password incorrect' }
    • Setting the failureFlash option to true instructs 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 a logout() function on req that can be called from any route handler which needs to terminate a login session. Invoking logout() will remove the req.user property and clear the login session (if any).

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 行即會顯示在loginregister模板中

參考文件