Serverless 與 JWT

深入 Serverless 中介紹了 serverless 基本的使用方法,若有一定了解之後可以來做更進一步的應用。本篇會試著使用 serverless 與 JWT (JSON Web Token) 來實作使用者驗證這個部分,因為我是使用 nodejs 來寫 serverless,所以在這裡會介紹到 serverless 使用 nodejs 時若有使用到其它 node modules 會需要注意的地方。andyyou 大在 Node 實作 jwt 驗證 API 中對 JWT 有深入的介紹,這裡僅做粗淺的介紹與 node-jsonwebtoken 的介紹及使用方法。

serverless v0.5.5

Why JWT?

使用者驗證在網路世界中是一個滿普遍的功能,舉凡任何需要會員登入的地方都會需要。但 http 連線是 stateless 的,所以當時的登入狀態其實只能在當下的頁面使用,一換頁便會失去這個狀態然後要重新登入。為了滿足換頁還保持登入狀態的需求,開始有了將登入的狀態保存在本地端的機制,這個登入狀態是經過加密的使用者資訊,在每次發送請求時將這個狀態送至伺服器端,讓伺服器來判斷使用者現在是否為登入狀態,而 JWT 就是用來協助實作這個機制加解密的部份。

JWT 簡介

JWT 是一個輕量且標準化的 token ,使用者在使用 JWT 時可將 token 帶在 request 的 headers 中或是以參數方式直接帶在 url 後面,像是:

http://www.mysite.com/?token=eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoiYSJ9.QnQzrcuJD9s5MHquMQ05NOsTp19fXjvkwzCnOkcAZVk

其中可以看到 token 是一加密過後的字串,中間用了 “.” 來做區隔,這就是 JWT 的基本架構,依順序所代表的分別是 Header、Payload、Signature。

  • Header:描述 token 使用的雜湊演算法 (預設為 HS256,也可選擇 RSA)。
  • Payload:token 的核心部分,使用者可在此自行加入 Claims 來設定 JWT,其中也有些 Claims 的名稱是預先定義好的,使用者可依其名稱來做設定,payload 的長度與在此設定的資料成正比。
  • Signature:由第一部份及第二部份來產生的簽章,被用來檢查 JWT 是否合法。

Claims:

是在 payload 中有特殊定義的 keys:

  • iss:token 發行者。
  • exp:token 的有效時間,使用 Unix Timestamp。
  • iat:JWT 所發行的時間,可以看出這個 JWT 被使用了多久。
  • nbf:“not before”,設定 token 要在未來的哪個時間才啟用。
  • jti:JWT 的唯一識別,避免 token 重複使用。
  • sub:token 標題(很少用)。
  • aud:token 讀者(很少用)。

node-jsonwebtoken

安裝:

$ npm install jsonwebtoken --save-dev

載入:

var jwt = require('jsonwebtoken');

jwt.sign(payload, secretOrPrivateKey, options, [callback]):

用來產生 JWT,payload 用來設定上面所介紹的 Header 與 Payload。secretOrPrivateKey 為一組字串來當作加密的 key,也可以是從外部載入的認證資料。

jwt.verify(token, secretOrPublicKey, [options, callback]):

用來驗證 token 是否正確,secretOrPublicKey 要與在註冊時所用的 secretOrPrivateKey 一致驗證才會通過。

jwt.decode(token [, options]):

用來解析當初在產生 JWT 時的設定。

結合 Serverless

在做使用者驗證的時候主要會有兩個功能,這裡使用 serverless 的方式來實作,完整程式碼放在 github 上。

  1. 使用者輸入密碼來取得 token。
  2. 驗證 token 是否合法來決定存取權限。

因應這兩個功能分別建立了兩個 endpoint,login 與 check。

在建立 endpoint 時,需考慮 request 與 response 的參數。例如在 login 時,會需要將使用者的密碼帶給伺服器端,並在密碼驗證成功後取的 token,並將 token 存入本地端。可以想像 login 的 http method 應為 POST,並會回傳參數。所以先建立 s-templates.json 設定 request 與 response 的參數,並將其帶入至 s-function.json 中。

login/s-templates.json

login/s-function.json

通常使用者密碼會存在資料庫中,這裡避免牽扯到資料庫的部分所以先直接以變數的方式來寫入。在功能方面一開始需判斷使用者帶來的密碼是否正確,若正確的話便使用 JWT 來產生一個 token 返回給使用者。

login/handler.js

接著來確認 check 的 request 與 response,在這裡會把剛剛拿到的 token 放在 headers 中來發出 request,所以 check 的 http method 應為 GET。在驗證完後返回一個狀態值給使用者來確認成功與否。在設定完 check 中的 s-templates.json 後一樣將設定值寫入對應的 s-function.json 中。

check/s-templates.json

check/s-function.json

在功能方面一開始先檢查 request 有沒有帶 token 來,若有的話使用 JWT 來驗證 token 是否正確,並將驗證結果傳回給使用者。

check/handler.js

測試

當設定跟程式都寫好後可以使用 Postman 來做測試。一開始先來測試 login,將路徑與 http method 都設定好後,在下方把要傳遞的資料格式輸入,設定好後按下 Send 會發送 request 到設定的路徑。

怎麼發生錯誤了?錯誤的原因是找不到 jsonwebtoken 的 module。

這裡有很重要的一點要注意,當有載入 node module 的時候,會需要到對應的 s-function.json 中去更改設定,把 handler 的值改成完整的路徑。這樣 function 在執行時才找得到所載入的 node module。

設定好後重新部署後再次回到 postman,再來發送 request 就會成功了。下面 Body 的部份取回了 token。

取得的 token 通常會存在瀏覽器的 localStorage 中,但這裡未使用到瀏覽器,所以直接先把 token 記下來,之後另外準備一個對 check 的測試。把路徑與 http method 參數設定好後,將 token 寫在 Headers 中,最後發送 request 後就可以看到回傳結果了。

參考

Node 實作 jwt 驗證 API

learn-json-web-tokens

jwt.io

RFC 7519

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.