--- tags: Node.js 直播班 - 2022 春季班 --- # 🏅 Day 30 ## JWT 驗證 [JWT 流程圖](https://whimsical.com/jwt-UKUY1rj1vfoN6uyic7e4Sm) ![](https://i.imgur.com/C2fj19V.png) 在前幾天的每日任務中,實做了註冊、登入功能,若成功註冊及登入的使用者會取得 JWT,也就是流程圖中的 1 - 3 步驟 接下來會實作 4 - 6 步驟,當需要登入才能造訪頁面或操作功能, client 端發出請求時就會需要在 header 的`Authorization` 帶上 JWT(`Bearer xxxxxx`),並經由後端驗證 而後端驗證的部分會使用 jsonwebtoken 套件的 `vertify()` 方法 ```javascript const jwt = require('jsonwebtoken'); jwt.verify(token, secretOrPublicKey, [options, callback]) ``` - token 為使用者夾帶需驗證的字串 - secretOrPublicKey 需帶入先前的環境變數 `process.env.JWT_SECRET` 因驗證 token 需要時間,jwt 這裡沒有提供 Promise,因此會需要另外使用 Promise 處理驗證結果 範例 ```javascript const decoded = await new Promise((resolve,reject)=>{ jwt.verify(token,process.env.JWT_SECRET,(err,payload)=>{ if(err){ reject(err) }else{ resolve(payload) } }) }) ``` ### 參考資源 [jwt.verify()](https://github.com/auth0/node-jsonwebtoken#jwtverifytoken-secretorpublickey-options-callback) ### 題目(將答案寫在 GitHub 並提交至回報區) **將驗證 JWT 過程設計為一個 isAuth middleware,驗證前端傳送過來的 JWT,並回傳驗證成功訊息** 情境:當使用者發出 GET `/users/test` 請求時,需先以 `isAuth()` 驗證前端夾帶的 JWT 是否正確,若正確則回傳成功訊息 :::spoiler 回傳訊息參考 ![](https://i.imgur.com/LKIZF8y.png) ::: -- - 需使用先前完成的註冊功能,註冊一個測試的使用者資料,並成功取得回傳的 JWT - 將此 JWT 帶入 isAuth middleware 驗證 **middleware 範例** ```javascript const isAuth = handleErrorAsync(async (req, res, next) => { let token = // 請帶入註冊成功回傳的 JWT // 驗證 token 正確性 /* 請在此參考上方做法驗證 JWT */ // const currentUser = await User.findById(/* 帶入驗證 token 解碼後取得的使用者 id */); // 在 req 物件加入 user 欄位,並由 next() 帶到 handleErrorAsync(async(req,res,next)=>{...}) req.user = currentUser; next(); }); ``` 將 middleware 加入測試的 router 中 ```javascript // routes/users.js router.get('/test', isAuth, handleErrorAsync(async(req,res,next)=>{ res.status(200).json({ status: 'success', user: req.user }); })) ``` 回報流程 --- 請同學依照下圖教學觀看解答、回報答案: ![](https://i.imgur.com/QtL8zEW.png) 回報格式:請在「回報區」貼上 CodePen 或 HackMD 連結回報答案 (為了統計人數,請同學依序加上「報數」) <!-- 解答 範例參考 https://github.com/gonsakon/express-week4-sample/blob/week6/service/auth.js ```javascript // isAuth middleware const isAuth = handleErrorAsync(async (req, res, next) => { let token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjYyN2IxNTlhMDlhZmMwZTU4NzZiNTE5MSIsImlhdCI6MTY1MjIzMzkwMCwiZXhwIjoxNjYwMDA5OTAwfQ.07q6Ib5tSZ1CYJLg-SaGIdGUnuyDaHY6wX9NnuM23io' // 測試用 JWT // 驗證 token 正確性 const decoded = await new Promise((resolve,reject)=>{ jwt.verify(token,process.env.JWT_SECRET,(err,payload)=>{ if(err){ reject(err) }else{ resolve(payload) } }) }) const currentUser = await User.findById(decoded.id); req.user = currentUser; next(); }); // routes/users router.get('/test', isAuth, handleErrorAsync(async(req,res,next)=>{ res.status(200).json({ status: 'success', user: req.user }); })) ``` --> 回報區 --- | 報數 | 組別 / 名字 | codepen / hackMD / 其他回饋 | | ---- | ---------------- | ------------------------------------------------------------- | | 1 | 第 14 組|East | [HackMD](https://hackmd.io/paLlXoQeQGaZwZIaf1EsVw) | | 2 | 第 5 組@Hazel | [HackMD@Hazel](https://hackmd.io/@hazelwu/day30) | | 3 | 第 11 組 Han Lai | [HackMD](https://hackmd.io/ghxn7a7XT5-fBbeMtjfVEg?view) | | 4 | 第 4 組 苡安 | [HackMD](https://hackmd.io/J_TYWJ4TRaGTIWLok0aWow) | | 5 | 第 3 組 hiYifang | [HackMD](https://hackmd.io/@gPeowpvtQX2Om6AmD-s3xw/S1WEL_yPc) | | 6 |第 9 組 / konstante |[HackMD](https://hackmd.io/JIcWC1IsRCeZux0ba3xvXQ?edit) | | 7 | 第 4 組 小宥 | [HackMD](https://hackmd.io/db9UrHNpTe6OyNTqzB-0jA) | | 8 | 第 9 組 / 黃士桓 | [HackMD](https://hackmd.io/3clCUx-9RteOyLMTZ3b6Lg) | | 9 | 第 3 組 / Hobby | [HackMD](https://hackmd.io/@hobbyling/day30) | | 10 | 第 2 組 / joe | [HackMD](https://hackmd.io/uaRTw9c0TgOrADyXMmGL0Q?view#520-auth) | | 11 | 第 12 組 / Jimmy | [HackMD](https://hackmd.io/_4hQX_ImTMiM-Z4l4gCYyg) | | 12 | 第 2 組 / wendy | [HackMD](https://hackmd.io/80FIwll1QUa4u0-WoBv-hQ#20220519)| | 13 | 第 16 組 / 皓皓 | [HackMD](https://hackmd.io/@cutecat8110/rkizhCF9q)|