# 面試系列文 - 技術面試考題 ## Router & Middleware 下方是一組路由設定檔,讓我們用 KEVIN / JOHN / JIMMY / MARY 來代稱四組設定。 ``` const express = require('express') const app = express() // KEVIN app.use((req, res, next) => { let name = 'Kevin' req.name = name next() }) // JOHN app.post('/me', (req, res, next) => { let name = 'John' req.name = name next() }) // JIMMY app.use('*', (req, res, next) => { let name = 'Jimmy' req.name = name next() }) // MARY app.get('/me', (req, res, next) => { let name = 'Mary' req.name = name next() }) app.use(function(req, res, next) { res.json({ answer: req.name }) }) ``` 請分別回答下列情境中 1. 呼叫 POST /me 時,最下方的 res.json({ answer: req.name }) 會印出什麼?KEVIN / JOHN / JIMMY / MARY 執行了哪幾個? 2. 呼叫 GET /me 時,,最下方的 res.json({ answer: req.name }) 會印出什麼?KEVIN / JOHN / JIMMY / MARY 執行了哪幾個? 3. 呼叫 GET / 時,,最下方的 res.json({ answer: req.name }) 會印出什麼?KEVIN / JOHN / JIMMY / MARY 執行了哪幾個? 承上,請你先查過 Express.js 文件裡對 app.use 和 next() 的說明,然後運用這兩個概念來解釋上題的各區塊是怎麼被觸發的?(app.use 和 next 兩個概念都需要代入) 1. JIMMY,KEVIN / JOHN / JIMMY 2. MARY , KEVIN / JIMMY / MARY 3. JIMMY , KEVIN / JIMMY ### Q16 回答(5) 首先, 1. 程式由上而下依序執行 2. app.use([path,] callback [, callback...]),文件中提到如果沒有設定path,則會預設為"/",對於 app 所有 根目錄 request 皆會執行 middleware 3. 如果呼叫 next() ,則會呼叫下一個middleware的函數 4. app.use('*', ...),對於 app 的所有 request 皆會執行 middleware > 呼叫 POST /me 時,最下方的 res.json({ answer: req.name }) 會印出什麼?KEVIN / JOHN / JIMMY / MARY 執行了哪幾個? 1. app.use , req.name = 'Kevin' , next() 2. app.post /me , req.name = 'John' , next() 3. app.use , req.name = 'Jimmy' , next() 4. app.use, res.json ,最後印出 Jimmy > 呼叫 GET /me 時,,最下方的 res.json({ answer: req.name }) 會印出什麼?KEVIN / JOHN / JIMMY / MARY 執行了哪幾個? 1. app.use , req.name = 'Kevin' , next() 2. app.use , req.name = 'Jimmy' , next() 3. app.get /me, req.name = 'Mary' , next() 4. app.use, res.json ,最後印出 Mary > 呼叫 GET / 時,,最下方的 res.json({ answer: req.name }) 會印出什麼?KEVIN / JOHN / JIMMY / MARY 執行了哪幾個? 1. app.use , req.name = 'Kevin' , next() 2. app.use , req.name = 'Jimmy' , next() 3. app.use, res.json ,最後印出 Mary ### 補充 #### 問題 請問助教, app.use ("/", ...) : 根目錄下(可能受route資料夾router匯集影響) app.use ("*", ...),所有路徑 (不受route資料夾影響) 是這樣做區分嗎? #### 助教回答 "*" (wildcard) 是正則表示式的一個特殊字元,用以表示符合所有字元,如果像是 "a*",則能符合 a1, a/b/c, asia...,這比較像是經過比對後,所有的路由皆符合 * 的比較,因此皆會執行 而 "/" 屬於 express 的判斷機制,當 app.use "/",則皆會執行該 middleware 兩者皆會受到 router 的影響 注意,並非與 route 資料夾有關,而是與 app.use('path', router)有關,像下述內容並沒有另外再切割程式碼到 route 資料夾下,會建立 route 資料夾主要是希望將 router 相關 module 都整理在 route 資料夾下以便於維護查找 ``` const app = express() const routeA = express.Router() routeA.get(...) routeA.use(...) const routeB = express.Router() routeB.get(...) routeB.use(...) app.use('a', routeA) app.use('b', routeB) ``` --- ## JWT 在 JWT 機制中,token 攜帶了哪些資料?為什麼成功登入後,只要 request 攜帶 token,伺服器就能驗證登入使用者? ### 回答(5) > token 攜帶了哪些資料? JWT 是一組字串,透過(.)切分成三個為 Base64 編碼的部分: * Header * Payload * Signature ``` // base64(Header) + base64(Payload) + base64(Signature) // xxxxx.yyyyy.zzzzz ``` * Header Header 是一個包含定義 Token 種類(type)及雜湊演算法(alg)資訊的 JSON。在此設定 Token 種類為 JWT、產生簽章(signature)要使用的雜湊演算法為 HS256。此 JSON 將被轉換成 Base64 編碼,成為第一個部分: ``` { "typ": "JWT", "alg": "HS256", } ``` * Payload(Claim) Payload 也是一個 JSON,可以解釋為要壓縮的資料,使用者和相關的資訊都可放置其中。通常會使用 exp 設定 Token 到期的時間、iat 設定 Token 簽發時間。最後再被轉換成 Base64 編碼,成為第二個部分: ``` { "_id": "<user_id>", "name": "John", "exp": 1300819999 } //建議只放必要且不隱私的資訊,不建議放過多資訊 ``` * Signature Signature會是 Payload 和 Header經過一組密鑰(Secret),加密(序列化)而成的字串。由於密鑰(ex. Secret: THISISSECRET) 並非公開,儲存在伺服端,因此伺服器端在拿到 Token 後,透過解碼,確認資料內容正確未被變更,以驗證對方身份。 ``` var payload = { id: user.id } //(Base64(Header) + "." + Base64(Payload), secretKey) var token = jwt.sign(payload, 'THISISSECRET') ``` 任何人都可以修改 JWT 的內容,但是當他簽發的時候並不知道密碼,所以就會有不對的簽名,伺服端也就不會接受這個錯誤的 JWT。 > 伺服器如何驗證使用者? 伺服端收到客戶端發送的token後,會進行反序列化,先將JWT的 Payload 和 Header 解析後,再和伺服端的Secret序列化後,得出新的Signature,與原本的Signature進行比對。如果相同,則此token有效完成身分認證,回傳200,才可以使用 Payload 的數據;如果不同,則此客戶端可能具有危險,回傳403。 ###### tags: `面試大哉問`