在 Angular 中解碼 JWT 權杖意味著將這個精簡格式的權杖拆分為三個 Base64url 區段,使用 window.atob 解碼標頭與負載,然後將結果解析為 JSON,讓已註冊與自訂的聲明資料變成可讀的 TypeScript 物件——這個過程只需要幾行程式碼,不需要任何外部函式庫。

Angular 開發者通常會在需要解碼權杖時遇到這個情境:HTTP 攔截器已經將 JWT 附加到外送請求上,而某個元件、守衛或服務需要知道權杖裡裝了什麼。常見的情境包括:從負載讀取主體、角色或過期時間,以決定是否要顯示某個功能、重新導向到登入頁面,或是排程一次靜默重新整理。由於標頭與負載僅經過 Base64url 編碼,並未加密,因此任何持有該權杖的人都可以在瀏覽器中讀取它。這個操作本質上就是一個字串轉換:依點號切開、將前兩個區段解碼、再呼叫 JSON.parse。這種簡單性也正是「解碼」永遠不等同於「驗證」的原因——它不會檢查簽章、不會檢查發行者、也不會檢查受眾,因此一個可讀的負載無法證明是誰簽署了這個權杖,也不代表它值得信任。

how to decode jwt token in angular
how to decode jwt token in angular

精簡 JWT 的三個區段

精簡格式的 JWT 是一個由點號分隔、恰好包含三個區段的字串,如同 RFC 7519 第 7.2 節所定義:header.payload.signature。當 Angular 的 HttpInterceptor 將權杖附加到請求上時,整個字串會作為 Bearer 憑證一起送出。第一個區段是標頭,是一個小型 JSON 物件,通常會標示簽章演算法與權杖類型。第二個是負載,是一個 JSON 物件,內含發行者選擇加入的聲明資料。第三個是簽章,是用金鑰對前兩個區段進行簽署後的結果,也是唯一能證明真實性的部分。

標頭與負載都是使用 RFC 4648 中所定義的 URL 安全 Base64 字集來編碼,該字集使用 A–Z、a–z、0–9、連字號與底線這些字元。標準的填補字元會被移除。由於這個字集是可逆的,將前兩個區段解碼回 JSON 並不需要任何密鑰。第三個區段對任何只做解碼的工具來說,都視為不透明的位元組;只有知道簽署金鑰的驗證步驟才能確認它的真偽。

Angular 會將權杖儲存在幾個典型的地方:記憶體中的 AuthService、sessionStorage、localStorage,或是由後端設定的 HTTP-only cookie。在前三種情況下,單頁應用程式可以取得原始字串並加以解碼。最後一種情況下,JavaScript 執行環境完全看不到權杖,因此解碼必須在伺服器端進行,或者乾脆略過。

為什麼 Angular 程式碼需要深入檢視權杖

解碼負載在幾個日常 Angular 情境中非常有用:從自訂聲明中顯示已登入使用者的顯示名稱、檢查過期時間以決定是否要排程靜默重新整理、讀取租戶或範圍聲明來決定是否放行某個路由,或是在偵錯攔截器時將解碼後的物件記錄下來。單元測試也能受惠於能夠斷言假權杖帶有哪些聲明資料,而無需引入完整的驗證機制。這些任務都不需要檢查簽章,只需要 JSON 是可讀的。

當開發者第一次在 Stack Overflow 上看到詢問「如何在 Angular 中解碼 JWT」的討論串時,被採納的答案幾乎總是一段四行的 atob 程式碼片段。這段片段足以讀取負載。然而它並不足以判定權杖是否有效,將兩者視為等價則是與這個主題相關最常見的安全錯誤。在任何正式環境的程式碼使用這些聲明資料來決定路由或顯示內容之前,權杖也應該交由一個知道簽署金鑰的函式庫來進行驗證。

RFC 7519 所定義的標準已註冊聲明,是大多數 Angular 程式碼在解碼後最常讀取的內容:

ClaimTypeMeaning
issstringIssuer — the principal that issued the token
substringSubject — the principal that is the subject of the token
audstring or arrayAudience — the recipients the token is intended for
expNumericDate (seconds)Expiration time — the time on or after which the token must not be accepted
nbfNumericDate (seconds)Not before — the time before which the token must not be accepted
iatNumericDate (seconds)Issued at — the time at which the token was issued
jtistringJWT ID — a unique identifier for the token, useful for replay prevention

NumericDate 的值是 Unix 秒數,而非毫秒。許多 Angular 應用程式會把兩者搞混,把 iat 或 exp 當作毫秒時間戳處理,結果得到一個落在數十年後或數十年前的日期。使用 new Date(exp * 1000) 來轉換 exp 才是正確的寫法;先除以 1000 再乘回去,則是一個常見的錯誤。

如何使用 atob 在 Angular 中解碼 JWT 權杖

在 Angular 元件或服務中解碼權杖,是一連串明確且精簡的步驟。瀏覽器的 window.atob 負責處理 Base64 的部分,JSON.parse 則將結果轉換成範本可以讀取的物件。atob 函式預期使用標準的 Base64 字集,因此需要做一點替換,因為 JWT 使用的是 URL 安全的變體。下列步驟涵蓋從已儲存的權杖一路到準備好供範本或守衛使用的具型別物件的完整路徑。

  1. 擷取權杖,從你的認證流程所使用的儲存層取得。可以從 AuthService 訊號、從 sessionStorage.getItem('access_token'),或從已經在請求上擁有權杖的 HttpInterceptor 中讀取。在繼續之前,先確認該值是非空字串。
  2. 以點號字元切分,並確認結果恰好有三個區段。任何不是三個區段的情況,都代表這個輸入不是精簡格式的 JWT,解碼應該以錯誤中止,而不該靜悄悄地回傳部分結果。
  3. 將 URL 安全的字集轉譯為標準 Base64,做法是把連字號換成加號、底線換成斜線。標頭與負載區段通常未經填補,因此在呼叫 atob 之前,應補足正確數量的等號,讓長度為 4 的倍數。這個步驟是 Angular 程式碼中最常出現 InvalidCharacterError 的地方。
  4. 使用 atob 解碼標頭與負載,並將每次呼叫包進 try/catch 中。格式錯誤的權杖會拋出例外,外層的服務應該將這個例外視為「無法使用的權杖」,而不是讓整個路由當機。
  5. 使用 JSON.parse 解析每個解碼後的區段,並將結果指派給一個具型別的結構。在 auth 資料夾中定義 JwtHeader 與 JwtPayload 介面,能讓其餘程式碼保有型別安全,並讓編輯器在 iss、sub、aud、exp、nbf、iat 與 jti 上提供自動完成。
  6. 透過訊號或 observable 將負載開放出來,讓應用程式的其他部分能讀取它。Angular 範本可以直接使用內建的 json pipe 來呈現聲明資料,它底層呼叫的是 JSON.stringify,是在開發期間快速確認解碼是否成功的好方法。
  7. 完成檢查後登出並移除權杖,包括輸入端以及剪貼簿或開發者控制台中的殘留。權杖經常含有個人識別資訊、租戶資料或範圍值,不應該留在螢幕擷圖、聊天訊息或分享的螢幕錄影中。

同樣的邏輯可以收納在一個輔助服務裡,讓元件與守衛不需要重複實作字集轉換的步驟。一旦這個輔助服務存在,Angular 專案中每個消費者都可以呼叫 decodePayload(token),並取回一個具型別的 JwtPayload。

使用本機 JWT 解碼器進行快速檢視

如果目的是在開發期間檢視聲明資料,而不是把結果串接到某個功能,那麼瀏覽器型的 JWT Decoder 是更快的選擇。將精簡格式的權杖貼到頁面上,選擇解碼動作,標頭與負載就會以 JSON 物件的形式呈現,並個別標示出已註冊的聲明資料。第三個區段僅會檢查是否符合精簡格式的結構,絕不會被解讀為有效的簽章,這讓檢視過程保持誠實。

這個工具完全在瀏覽器中執行。權杖不會離開這個頁面,不會擷取任何簽署金鑰,造訪之間也不會儲存任何資料。這讓它成為檢視你有權查看之權杖的安全場所,無需將它送到第三方解碼器或公開 API。若想更深入地走過同一個流程,在瀏覽器中不寫程式碼解碼 JWT 權杖這份指南會從不同角度介紹相同的工具。

對於以 Unix 秒數表示的聲明資料,可以再快速跑一次 Unix Timestamp Converter,確認 exp 值是否與後端所設定的工作階段生命週期一致,同樣不需要離開瀏覽器。當解碼後的負載包含較長的自訂聲明值而需要讓它更易讀或檢查語法時,JSON Formatter 與 JSON Validator 也是實用的搭配工具。

解碼無法告訴你的事

解碼權杖是一個字串轉換,而不是安全檢查。標頭會告訴你發行者宣稱使用了哪一種演算法,但它無法確認簽章是否真的以該演算法產生。針對用戶端解碼器的常見攻擊是 alg-none 降級攻擊:標頭宣稱這是未受保護的權杖,而第三個區段為空。解碼器仍然會顯示一個看似完全有效的負載,因為解碼過程根本不會把第三個區段當作簽章來檢視。

exp、nbf 與 iat 的值是一種慣例,並不是保證。一個能成功解碼的權杖,可能已經過期、尚未生效、由應用程式不信任的一方所發行、預定給不同的受眾,或根本是偽造的。解碼器不會擷取 JWKS 端點、不會將發行者與允許清單進行比對、也不會將受眾與設定值進行比對。受眾聲明(可以是字串或字串陣列)會以 JSON 的形式呈現,但不會判斷它是否符合這個應用程式。NumericDate 值是一種表達時間的方式,並不代表該權杖目前在時效上是可被接受的;時鐘漂移、重新整理窗口,以及發行端的政策,全都落在解碼步驟之外。

自訂聲明——應用程式層在已註冊集合之外額外加入的私有聲明名稱——在原始負載中是可見的,但解碼器不會賦予它們任何特殊意義。即使某個私有聲明缺失或不如預期,解碼仍然會成功。JWE 也是一樣的情況:JSON Web Encryption 權杖無法透過這個工具讀取,也沒有任何替代方式能揭露加密內容。

When to Validate Instead of Just Decode

Decode is for reading. Validate is for trusting. The two operations belong in different layers of an Angular application. The decoded payload is fine for showing the user's name, deciding whether to show an admin link, or branching a route based on a role claim. The moment a decision grants access to data, calls a sensitive endpoint, or skips a login screen, the token should be verified by code that holds the signing key or that has just fetched it from the authorization server.

For HS256, RS256, and ES256 tokens, a maintained library such as @auth0/angular-jwt or jose handles verification in a few lines. The library checks the algorithm against an allow list, verifies the signature against the configured key, compares iss to the expected issuer, checks aud against the expected audience, and confirms that the current time falls inside the nbf to exp window with whatever clock skew the system requires. A short-lived access token plus a long-lived refresh token, both verified on the SPA at request time and on the backend at the resource server, is the standard shape for a single-page application. The decoded payload still helps the UI render faster, but it never makes the access decision on its own.

If the application cannot verify the token at all because it has no key, the right answer is to send the token to the backend and let the resource server make the trust decision. The SPA can decode to render a loading state, and the backend can return the real authorization verdict a few milliseconds later. The two together cover the common Angular cases where a decoded payload is useful without ever being mistaken for a proof of authenticity.

Related reading: Generate a JWT for Testing: HS256 Tokens in Your Browser.