跳到主要內容
楓葉工具箱 ToolMaple

開發最後更新:

JWT Decode 解碼

貼上 JWT 就拆開給你看 header 和 payload,過期時間會轉成看得懂的日期。

格式正確、無 exp
payload 沒有 exp 欄位、無法判斷有效期

自動檢查

  • 沒有 aud(受眾):任何信任這個簽發者的服務都會收下它。發給特定 API 的 token 通常要帶 aud,並由 API 檢查。

時間欄位

  • iat(簽發時間)
    1516239022
    1/18/2018, 1:30:22 AM

Header

{
  "alg": "HS256",
  "typ": "JWT"
}

Payload

{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}

Signature(base64url)

SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

本工具只解碼、不做簽名驗證(需要密鑰或公鑰)。

這個解碼器看得到什麼、看不到什麼

JWT(JSON Web Token)是 2015 年 IETF RFC 7519 定義的「以 JSON 表示、用簽章保護完整性」的 token 格式。常用於現代網站登入流程:使用者登入後,server 產生一個 JWT 給瀏覽器,之後每次 request 帶上這個 token,server 就知道是誰,不必查資料庫。

JWT 屬於更大的 JOSE(JavaScript Object Signing and Encryption)標準家族,相關規範還有 JWS(簽章)、JWE(加密)、JWA(演算法)、JWK(金鑰格式)。OAuth 2.0 / OpenID Connect / Auth0 / Firebase Auth / Supabase 都用 JWT。

JSON Web Token 由 . 分隔的三段組成: Header.Payload.Signature。前兩段是 base64url 編碼的 JSON、第三段是簽章。 本工具只解前兩段內容。

關鍵字是解碼。本頁不驗證簽章,也就無法告訴你這個 token 是不是真的。驗簽需要 HS256 這類 HMAC 演算法的共享密鑰,或 RS256、ES256、EdDSA 的發行者公鑰,這一頁兩樣都不跟你要。 你在這裡看到的是「發行者聲稱的內容」,讀 token 時這正是你要的, 但拿來判斷該不該信任它,遠遠不夠。

順帶一提大家手寫的那一行:JSON.parse(atob(token.split(".")[1]))有兩個不容易發現的坑。base64url 把 + 與 / 換成 - 與 _ 並去掉 = 補位,atob 不是丟例外就是還原出垃圾;而且它一個字元只還原一個位元組, 名字裡有重音字母、中文、日文或 emoji 就會變成亂碼。本頁會先把字母表換回來、補上等號,再把位元組當 UTF-8 解。

哪個欄位回答哪個問題

解出來一整包 JSON,實務上你多半只需要看其中一兩個欄位。照症狀挑: 401 先看 exp 與 iss、aud;403 看權限相關的 scope 或 role;伺服器說簽章驗不過,看 header 的 alg 與 kid;要查「這是誰」就看 sub。

常見 payload 欄位:

  • iss(issuer):發行者
  • sub(subject):主體(通常是 user id)
  • aud(audience):接收方
  • exp(expiration):過期時間 Unix timestamp
  • iat(issued at):發行時間
  • nbf(not before):早於這時間視為無效
  • jti(JWT ID):token 唯一識別碼

簽章演算法(看 header 的 alg):

  • HS256 / HS384 / HS512(HMAC):用「共享密鑰」簽。簡單、適合 server 自簽自驗、不適合跨服務(雙方都拿到 key 就能偽造)
  • RS256 / RS384 / RS512(RSA) :用私鑰簽、公鑰驗。適合「server 簽、其他人驗」的單向信任場景。OIDC 預設用 RS256
  • ES256 / ES384 / ES512(ECDSA):橢圓曲線。比 RSA 短、效能好、新系統推薦
  • none:不簽章。曾經是 JWT 規範漏洞之一(攻擊者把 alg 改成 none 就能繞過驗證),現代 library 預設禁用

工具下方的 Signature 區塊只用來確認簽章存在、不是空的。第三段是空的,代表這個 token 是用 alg: none 發出來的,現行的函式庫都不該接受;header 寫著 none 時,「自動檢查」會跳出警告。

更上游的選擇:JWT 還是 Session Cookie?

  • Session Cookie:server 存 session、cookie 只存 session id。優:可立即撤銷、payload 小;劣:要 sticky session 或共享 store
  • JWT:所有資訊在 token 內、server stateless。優:易做水平擴展、跨服務;劣:難立即撤銷(要靠 blacklist 或縮短 exp),payload 較大
  • 實務:純單體網站用 session cookie 通常更簡單;微服務 / SPA / 跨網域 / OAuth 場景用 JWT 更自然

真的需要驗證而不只是解碼時,請在伺服器端用函式庫做:JavaScript 用 jose(驗簽與遠端金鑰組都包好),Python 用 PyJWT,Go 用 golang-jwt。

手上有 token,怎麼查 401 的原因

  1. 從瀏覽器開發者工具的網路面板、或發出這個請求的 API 工具,複製 Authorization 標頭裡的 token。前面的 Bearer 與那個空格不要一起複製。
  2. 貼進上面的輸入框。如果解不開,問題通常在複製而不在 token 本身:找找中間是不是斷行了,或是從網址列複製時留下的百分比編碼。
  3. 先讀狀態列。顯示已過期,那你要修的是換發流程,不是驗證流程。
  4. 比對 iss 與 aud 跟這支 API 期望的值。環境搞混是「token 解得開卻回 401」最常見的原因;payload 完全沒有 aud 時,「自動檢查」會提醒你。
  5. 對照發行者公布的金鑰組檢查 alg 與 kid。金鑰輪替後服務端快取太久,症狀就只有一個簽章驗不過。
  6. 最後才看 payload 裡端點要的 scope 或 role。權限不足通常回 403,所以你拿到的是 401 的話,原因在上面幾步。

不用自己換算,直接讀過期時間

  1. 貼上 token。上方橫幅會說 exp 過了沒,還有剩多久或過期多久。
  2. 看「時間欄位」那三張卡,原始的 Unix 秒數就印在本地日期旁邊。 某個數字大了大約一千倍,代表發行端把毫秒填進了 RFC 7519 規定是秒的欄位,標準的驗證器一律會判錯;「自動檢查」會直接點名是哪個欄位。
  3. exp 減 iat 就是這張 token 的壽命,「自動檢查」會把它換算好印出來。access token 常見是 15 分鐘到 1 小時;看到一年的,值得回頭問一句,因為它收不回來。
  4. 完全沒有 exp 時,工具會直說而不是亂猜。沒有過期時間的 token,會一直有效到簽章金鑰被輪替為止。
  5. 記得這裡的判斷用的是你裝置的時鐘,伺服器用它自己的。這就是為什麼 token 有時候會早一分鐘或晚一分鐘失效。

把解出來的內容貼進工單或訊息

Header 與 Payload 右上角各有一顆複製鈕,複製到的是排版過的 JSON,可以直接放進 pull request、issue 或聊天室的程式碼區塊。送出去之前先想一下:這些 claim 描述的是一個真實的人,email、user id、租戶名稱、角色清單都很常見,每一項都算個資或商業敏感資訊。

  • 貼解出來的內容,不要貼 token 本身。把你要討論的欄位留著,其餘換成佔位字串。token 是活的憑證,解出來的結構通常不是。
  • Android:Gboard 會留剪貼簿歷史,token 經過那裡的話記得回去刪掉。
  • 正式環境的 token 不要貼進 LINE、Slack 或 Discord。訊息可以被搜尋、會被備份,常常保存好幾年。真的要示範,就拿測試帳號簽一張,或用一張已經過期的。

誰會發 JWT、各家長得不太一樣

常見使用情境:

  • SPA / 行動 App 登入:server 產生 JWT,client 存 localStorage 或 cookie,後續 request 放 Authorization: Bearer ...
  • OAuth 2.0 / OIDC:Google / Facebook / Apple 登入 成功後拿到的 id_token 就是 JWT
  • 跨服務驗證:micro-service 間互信,用共享密鑰簽 JWT 傳遞使用者身分
  • Email 驗證 / 重設密碼連結:把過期時間放在 token 裡,不必在資料庫多存一個記錄
  • API key 替代:第三方 API(Stripe webhook、Apple sign in)用 JWT 證明請求來源合法

每張 JWT 都照 RFC 7519,但註冊欄位以外要放什麼,各家自己決定。撰寫當下的情況:

  • OpenID Connect 供應商:發的 id_token 是描述這次登入的 JWT,多半用 RS256 簽;另外那張 access token 的格式規範沒有規定,不一定是 JWT。 公鑰與支援的演算法列在各家文件寫的探索文件與金鑰組網址。
  • LINE Login:id_token 的 iss 是 https://access.line.me、aud 是 channel ID、sub 是 LINE 的使用者 ID;網頁登入用 HS256(以 channel secret 簽),原生 App 與 LIFF 用 ES256。LINE 另外提供驗證 ID token 的 API 端點,伺服器端可以直接呼叫它驗。
  • Auth0:自訂 claim 必須加 URI 命名空間,所以角色清單會出現在類似 https://example.com/roles 的鍵底下,而不是單純的 roles。自訂 claim 憑空消失,通常就是忘了加命名空間。
  • Firebase Authentication:ID token 用 RS256 簽,sub 是 Firebase UID、aud 是專案 id,另外有一個 firebase 物件記錄使用者用什麼方式登入。
  • Supabase:access token 的 sub 是 user id,另有一個 role,Postgres 的 row level security 透過 auth.uid() 讀它。舊專案用專案密鑰對稱簽,新專案可以改用非對稱金鑰,先看你自己的專案設定。
  • Node:jsonwebtoken 把 decode 與 verify 拆成兩個函式是刻意的。在處理請求的程式碼裡呼叫 decode,是最典型的驗證繞過寫法。
  • Python:PyJWT 兩件事共用一個函式,jwt.decode(token, options={"verify_signature": False}) 是唯讀寫法,其餘情況都要給金鑰與明確的演算法清單。
  • Shell:cut -d. -f2 <<< "$TOKEN" | base64 -d 常常會失敗,因為 base64url 是不同的字母表而且去掉了補位。要先把 -_ 換回 +/,再補 = 到長度是 4 的倍數。

安全、隱私,與這頁做不到的事

  • JWT 內容是 base64 編碼,不是加密,任何人都能解出 payload
  • 不要在 payload 放敏感資訊(密碼、身分證、信用卡)
  • 過期 token 即使「看起來合法」也該拒絕(server 必須驗 exp)
  • 客戶端解碼只是預覽,server 端必須完整驗證簽章

伺服器端怎麼驗。一律用函式庫驗簽,一律傳入你預期的演算法清單而不是照 header 的 alg 走,並且一律檢查 exp、iss 與 aud。JWT 歷史上的攻擊就是從省略這幾步來的:把 alg 改成 none 重簽一張,或把 RS256 的 token 當 HS256 重放,讓公鑰被當成共享密鑰用。

token 收不回來。發出去的 JWT 在過期前都會被接受,因為驗證它不需要查任何資料庫。縮短壽命配 refresh token、維護 jti 黑名單、在 payload 裡放一個你能遞增的版本號,是三種常見的補救, 三種都要犧牲掉一部分當初選 JWT 看上的無狀態。

內容真的需要在傳輸與儲存時保密,那是 JWE 的工作,JWE 有五段而不是三段。

貼正式環境的 token 安全嗎? JWT 裡是帳號身分與權限資訊,外洩等於把登入狀態送人,所以這個問題該問。 本工具的「解碼」其實只是 Base64url 還原,一行 JavaScript 就能做的事,全程發生在你這個分頁,本站沒有任何後端 API 能收到它。要更保險,可以貼一個已過期的 token 來除錯,資訊結構相同。 不過這句話只涵蓋這一頁:同一張 token 出現在截圖、log 或聊天記錄裡,一樣是外洩。

常見問題

JWT 過期後該怎麼換新的?

常見做法是雙 token:短期 access token(15 分鐘到 1 小時)+ 長期 refresh token(數天到數週)。access token 過期時,用 refresh token 跟 server 換新的,使用者不會被踢出。refresh token 必須安全儲存(HttpOnly cookie)。

能撤銷已發出的 JWT 嗎?

嚴格說不行——這是 JWT 設計的取捨。實務做法:(1)縮短 exp、強制 refresh;(2)server 端維護一份 blacklist;(3)使用者登出時改 user 的「token 版本號」,JWT 內含此版本號,版本對不上即失效。

JWT 該存 localStorage 還是 cookie?

HttpOnly cookie 較安全(防 XSS 偷 token),但要處理 CSRF。localStorage 寫起來簡單,但只要網站有任何 XSS 漏洞就完蛋。SPA 普遍用 localStorage,安全敏感的應用建議改 HttpOnly cookie + CSRF token。

為什麼 JWT 看起來這麼長?

因為三段都是 Base64url 編碼,加上 payload 通常含 user id、權限、過期時間等多個欄位,再加簽章本身就要 256-bit 以上,總長度 200-500 字元很正常。Cookie 4KB 上限通常夠用,但要小心多個 JWT 同時存的情況。

這個工具會驗證簽章嗎?

不會。本頁只把前兩段 base64url 還原成 JSON,第三段簽章原樣顯示,不做任何驗證。驗簽需要 HS256 這類演算法的共享密鑰,或 RS256、ES256、EdDSA 的發行者公鑰,這一頁兩樣都不跟你要。所以這裡看到的是「發行者聲稱的內容」,不足以判斷這個 token 該不該被信任,那一步請在伺服器端用函式庫做。

為什麼我自己用 atob 解會亂碼或報錯?

手寫的 JSON.parse(atob(token.split(".")[1])) 有兩個坑。第一,JWT 用的是 base64url,把 + 與 / 換成 - 與 _ 並且去掉 = 補位,atob 不是丟例外就是解出垃圾。第二,atob 一個字元只還原一個位元組,名字裡有重音字母、中文、日文或 emoji 就會變成亂碼。本頁會先把字母表換回來、補上等號,再把位元組當 UTF-8 解。

token 解得開,API 卻還是回 401,該看哪裡?

照這個順序看:先看 exp 是不是過了,過了就是換發流程的問題而不是驗證的問題;再比對 iss 與 aud 跟這支 API 期望的值一不一樣,測試環境簽出來的 token 拿到正式環境用,解得開但一定過不了 audience 檢查;最後看 header 的 alg 與 kid 有沒有在發行者公布的金鑰組裡,金鑰輪替後服務端快取太久就會只表現成簽章失敗。權限不足通常回 403 而不是 401,所以 401 的原因幾乎都在這幾步之前。

資料來源

更多開發者工具

想單獨手解某一段、或任何 base64url 字串,用「Base64 編碼解碼」;payload 複製出來要排版、或想確認 API 回應本身解不解得開,用「JSON 格式化」。從 query string 撈出來的 token 可能還帶著百分比編碼,先過「URL 編碼解碼」還原;exp 那串秒數想單獨換算成日期,用「Unix 時間戳轉換」。要一組新的識別碼填 jti 或開測試帳號,用「UUID 產生器」。

相關工具

隱私:token 在你的瀏覽器內解碼,不會上傳、不會儲存、不會寫進紀錄,本站也沒有任何後端 API 能收到它。輸入與解出來的內容都對分析工具遮蔽,關掉分頁就沒了。