整理這些技術筆記真的很花時間,如果你願意 關閉 Adblock 支持我,我會把這份感謝轉換成更多「踩坑轉避坑」的內容給你!ヽ(・∀・)ノ
Day29-關於 JWT 驗證

前言
接下來這一篇將會來介紹 JWT 的驗證,畢竟前面一直都沒提到,可是實戰上來講卻是非常重要的一環哩。
身份認證
為什麼要身份認證呢?前面的許多章節中,其實我們都沒有做身份認證的機制,舉凡 API、Discord、Chrome Extension 等等,都沒有做身份認證,那麼為什麼要做身份認證呢?
我們所開發的東西有些功能是僅限於特定的人才能使用,例如:管理員、VIP 等等,這時候我們就需要一個機制來做身份認證,那麼這時候就會提到 JWT(JSON Web Token)了。
而現今主流開發比較常見於前後端分離的架構,不再是傳統的 SSR(Server Side Rendering)的架構,前端大多都是透過後端所提供的 API 來取得資料,而這個取資料的過程就必須要被認證。
Note
早期開發時,使用者登入後,後端會將使用者的 UID 儲存在伺服器的記憶體中(Session),但因為現今主流開發事前後端分離的架構,因此這種方式已經不適用了,因此就有了 JWT。
JSON Web Token 是什麼?
剛才有提到 JWT 的全名是 JSON Web Token,是一個廣泛被使用來驗證與授權的標準,尤其是在網頁開發上很常被使用,而且也是一個開放的標準(RFC 7519)。
基本上 JWT 的結構分為三個部分:
- HEADER(標頭):ALGORITHM & TOKEN TYPE
- 用來說明這一組 Token 是用什麼演算法加密的,以及 Token 的類型
- PAYLOAD(負載):DATA
- 主要是用來存放一些資料,例如使用者的 ID、名字等等
- VERIFY SIGNATURE(驗證簽名)
- 這一部分是用來驗證 Token 是否為正確的 Token
1 | |
很難看出其差異吧?如果我依據上面的結構來拆解的話,就會變成這樣:
1 | |
搭配上方的 JWT 範例,就會變成這樣:
1 | |
JWT 原理
那麼 JWT 是如何實作出來的呢?剛才有提到 JWT 的結構分為三個部分,其實比較核心的部分是 PAYLOAD(負載)與 VERIFY SIGNATURE(驗證簽名),但我們還是針對 JWT 三個部分來做介紹,而這邊示範的範例會是前面所講的 JWT Token 範例:
1 | |
HEADER(標頭)
HEADER 主要組合會是兩個部分:
- alg(Algorithm):加密演算法
- typ(Type):Token 類型
而這兩個部分會被編碼成 Base64 字串,例如:
1 | |
PAYLOAD(負載)
PAYLOAD(負載)的部分,其實就是一個 JSON 物件,例如:
1 | |
而這個 JSON 物件會被編碼成 Base64 字串,例如:
1 | |
感覺 PAYLOAD 滿單純的吧?但實際上它是由三個部分組成的,分別是:
- Registered Claim(註冊聲明)
- Public Claim(公開聲明)
- Private Claim(私有聲明)
而這三個部分都是選填的,但實戰上來講,通常都會使用 Public Claim(公開聲明)來存放一些資料,例如:使用者的 ID、名字等等。
比較常見的設定會是 Registered Claim 中的 iat(Issued At)與 exp(Expiration Time),而這兩個屬性可以用來驗證 Token 是否過期等。
當然,Registered Claim 中還有很多屬性,例如:
- iss(Issuer):發行者
- sub(Subject):主體
- aud(Audience):對象
- exp(Expiration Time):過期時間
- nbf(Not Before):生效時間
- iat(Issued At):簽發時間
- jti(JWT ID):JWT ID
因此以上面前面的 JSON 物件來說,就會變成這樣:
1 | |
Note
要注意一下 Claim(聲明)的單字只有三個字母,因為 JWT 旨在簡潔與輕量化,因此 Claim(聲明)的單字只有三個字母。
VERIFY SIGNATURE(驗證簽名)
VERIFY SIGNATURE(驗證簽名)的部分,其實就是將 PAYLOAD(負載)與 HEADER(標頭)進行編碼,然後再進行加密,例如:
1 | |
那麼問題來了,JWT 是如何知道要用什麼演算法來加密呢?其實就是透過 HEADER(標頭)來取得,例如:
1 | |
如果今天是用 PS256 演算法來加密的話,就會變成這樣:
1 | |
接著就會將 PAYLOAD(負載)與 HEADER(標頭)進行編碼,然後再進行加密,例如:
1 | |
而這個加密後的字串也會被編碼成 Base64 字串。
那 secret 是什麼呢?其實就是一個私鑰,我們在針對 JWT TOken 生成時,會需要使用一組私鑰,而這組私鑰會被用來加密,而當我們要驗證 JWT Token 時,就會需要使用這組私鑰來解密。
那麼另一個問題來了,我們生成的 JWT Token:
1 | |
你可以把它丟到 jwt.io 這個網站上,然後你就會發現它會自動幫你解析,並且告訴你這個 Token 是用什麼演算法加密的,以及 Token 的類型,如下圖:

好的,問題來了
「為什麼我們沒有提供私鑰(secret),但是它卻可以解析出來呢?」
因為 JWT Token 只是透過 Base64 編碼,而並沒有進行加密,因此你是可以自己寫一個 JWT Token 解析器,這邊我也簡單示範寫一個 JWT Token 解析器範例:
1 | |
接著你貼到瀏覽器上就可以看到解析結果了,如下圖:

因此實際上來講 JWT Token 是用 Base64 編碼的,而且有一定的安全性問題,所以千萬不要把敏感資訊放在 JWT Token 中,例如:使用者的密碼、使用者的信用卡資訊等等。
JWT Token 核心驗證的方式主要在於剛剛提到的 secret(私鑰),因此就算我們解析出來了,但是沒有私鑰的話,也是無法驗證成功。
那麼透過這一篇,我相信你應該對於 JWT Token 有一定的了解了,接下來我們就來看看如何實作 JWT Token 吧!
實作 JWT Token 發放
JWT Token 在 Node.js 上使用非常的簡單,只需要安裝 jsonwebtoken 套件
1 | |
用法也很簡單,只需要安裝 jsonwebtoken 套件並這樣寫就可以了:
1 | |
jwt.sign 會接受三個參數:
- payload:PAYLOAD(負載)
- secret:私鑰
- options:選項,也就是 Registered Claim(註冊聲明)
而 jwt.sign 會回傳一個 JWT Token,這個 JWT Token 就是我們要發放給使用者的 Token。
往後前端需要請求某些需要權限或驗證的 API 時,就可以將這個 JWT Token 放在 Header 中,例如:
1 | |
接著後端只需要使用 jwt.verify 就可以驗證 JWT Token 是否正確,例如:
1 | |
是不是超簡單的呢?這邊我就不額外提供範例了,你可以再試著自己嘗試看看。
Note
Bearer是一種 HTTP 認證方式,你可以參考 Authentication 這篇文章。
那麼這一篇也差不多了,我們下一篇見哩。
整理這些技術筆記真的很花時間,如果你願意 關閉 Adblock 支持我,我會把這份感謝轉換成更多「踩坑轉避坑」的內容給你!ヽ(・∀・)ノ