JWT 在 C# 中的解碼方式,是將精簡形式的權杖以「.」切分為三段 Base64url 字串,再對前兩段進行 JSON 解析——這與任何程式庫或瀏覽器輔助工具所執行的第一個步驟相同。標頭 (header) 帶有演算法識別碼與權杖類型,酬載 (payload) 帶有已註冊與自訂的聲明 (claims),而簽章段則保持原樣不動,因為驗證它需要由簽發應用程式所持有的簽署金鑰。在典型的 C# 程式碼中,你會使用 System.IdentityModel.Tokens.Jwt,並呼叫 JwtSecurityTokenHandler.ReadJwtToken,它會為你執行切分、Base64url 轉換與 JSON 解析,並回傳一個帶有強型別 Claims 集合的 JwtSecurityToken。如果你不想引入 NuGet 相依套件,同樣的處理工作只需幾行程式碼就能完成,使用 Convert.FromBase64CharArray 搭配 URL-safe 字元集,以及 System.Text.Json.JsonDocument 來走訪解析後的物件。這些步驟都沒有驗證簽章,因此解碼後的酬載只有在來自於你已掌控其金鑰與驗證原則的系統時,才能放心信任。像 JWT Decoder 這樣的本機瀏覽器輔助工具,則執行相同的純解碼操作,讓你無需上傳權杖即可快速讀取標頭與酬載的內容。

how to decode jwt token in c#
在 C# 中不解碼 JWT 權杖且無需 NuGet 程式庫

精簡 JWT 實際的樣貌

精簡序列化形式的 JWT 恰好是由句點連接的三段 Base64url 字串:header.payload.signature。標頭描述演算法與權杖類型,酬載帶有聲明,而簽章段則是簽發者以其簽署金鑰所產生的位元組。RFC 7519 定義了結構與已註冊的聲明名稱,而 RFC 4648 定義了 URL-safe Base64 的字元集,包含字母、數字、連字號與底線,精簡 JWT 即採用此字元集,且通常省略填補字元 (padding)。

你不需要簽章就能讀取權杖所表達的內容。在你將 URL-safe 字元集的字元替換回來,並把長度填補為四的倍數之後,你所關心的兩段都會是純粹的 UTF-8 JSON。C# 程式庫會為你處理這項轉換,當你將同一段精簡字串貼到瀏覽器時,JWT Decoder 也會這樣做。

在 C# 中逐步解碼 JWT 權杖

  1. 以字串形式擷取 header.payload.signature 格式的權杖,然後呼叫 Split('.') 以取得三個子字串。
  2. 取前兩個子字串,將 - 換成 +,將 _ 換成 /,並用 = 填補至長度為四的倍數,使 URL-safe 字串成為有效的標準 Base64。
  3. 將每段已填補的字串傳入 Convert.FromBase64String 以取得原始的標頭與酬載位元組,接著使用 Encoding.UTF8.GetString 將這些位元組解碼為 UTF-8。
  4. 將每段解碼後的字串交給 JsonDocument.Parse,並把 RootElement 視為 JSON 物件讀取,使結果能以型別安全的方式走訪。
  5. 從標頭檢查 algtyp,接著走訪酬載上的已註冊聲明 isssubaudexpnbfiatjti,並將每個值視為未經驗證的資訊。

包裝在一個小型輔助函式中時,同樣的五個步驟在 .NET 主控台專案中看起來像這樣:

var handler = new JwtSecurityTokenHandler();

var token = handler.ReadJwtToken(jwtString);

Console.WriteLine(token.Header.Alg);

foreach (var c in token.Claims) Console.WriteLine(c.Type + ": " + c.Value);

ReadJwtToken 呼叫會在內部執行第一至第四步,並公開已解析的標頭,以及一個已將已註冊名稱對應到 ClaimTypes 等價項目的 Claims 集合。若要走不使用程式庫的路徑,請將前兩行替換為你自己的 Base64url 轉 UTF-8 轉換,以及對每段自行呼叫 JsonDocument.Parse。最終結果是與任何解碼器中所見相同的標頭欄位與酬載聲明字典,而且你能掌控流經程式碼的每個位元組。

C# 中三種解碼方式的比較

方式所需的套件回傳結果簽章檢查
JwtSecurityTokenHandler.ReadJwtTokenSystem.IdentityModel.Tokens.Jwt帶有 Header 與 Claims 的 JwtSecurityToken否,除非你呼叫 ValidateToken
手動 Base64url + JsonDocument除了 System.Text.Json 之外無需任何套件標頭與酬載的兩個 JsonDocument 物件否,需要自行實作 HMAC 或 RSA 檢查
瀏覽器中的 JWT Decoder無,於本機執行解碼後的標頭與酬載 (以 JSON 表示),加上可辨識的已註冊聲明否,會將每個結果標示為未經驗證

不使用程式庫的方式與瀏覽器輔助工具看起來幾乎相同,因為兩者都僅止於解碼。差異在於程式碼執行位置,以及剖析器接受哪些輸入。C# 方法無法容忍多餘的等號符號,或是缺少其中一段的權杖;嚴謹的實作會將這些情況回報為錯誤,而不是自行猜測。JWT Decoder 採用同樣嚴格的規則:它會拒絕填補字元、要求剛好三個段落,並要求解碼後的標頭與酬載都能解析為 JSON 物件。

解碼後讀取標頭與酬載

標頭通常包含兩個欄位:alg,用於指定簽章演算法,例如 HS256 或 RS256;以及 typ,通常為 JWT。請將任何非預期的 alg 值視為警訊,因為演算法混淆是常見的 JWT 攻擊模式,合格的驗證器應在依據酬載內容採取行動之前拒絕該權杖。

酬載除了 RFC 7519 已註冊的聲明外,還可能帶有簽發者自行加入的自訂聲明。已註冊的名稱具有解碼器可辨識的特定意義:

聲明RFC 7519 名稱類型通常代表的意義
issIssuer (簽發者)String簽發該權杖的主體
subSubject (主體)String該權杖所指涉的主體,通常為使用者識別碼
audAudience (受眾)String 或陣列預期的接收者識別碼
expExpiration Time (到期時間)NumericDateUnix 秒數,超過此時間權杖必須被拒絕
nbfNot Before (生效時間)NumericDateUnix 秒數,在此時間之前權杖必須被拒絕
iatIssued At (簽發時間)NumericDate權杖簽發當下的 Unix 秒數
jtiJWT ID (JWT 識別碼)String用於防止重放攻擊的唯一識別碼

NumericDate 是自 Unix 紀元起的純整數秒數,這就是為什麼 expnbfiat 全部看起來都是十位數的數字。JWT Decoder 與大多數 C# 程式庫都會將同一個值同時呈現為原始整數與 ISO 8601 時間戳記,以便與系統時鐘進行比較。audience 聲明可能以單一字串或陣列形式出現,你必須將它與應用程式所預期的識別碼進行比對;解碼器本身並不會強制執行這項相符性檢查。完整的已註冊名稱集合定義於 RFC 7519,這是所有聲明預設意義的權威來源。

JWT Decoder 在 C# 程式碼中的搭配位置

當權杖存在於你的服務內部、你想讓解析後的值流入強型別物件,或你需要從同一個呼叫驅動驗證邏輯時,C# 解碼是合適的工具。然而在開發過程中,有許多時刻權杖只是出現在網路面板、日誌記錄或 Postman 回應中,而你只想讀取其內容。將該字串貼入 JWT Decoder 即可達成此目的:它會在你瀏覽器中切分相同的 three 段、執行相同的 Base64url 轉 UTF-8 轉換,並在不上傳權杖或聯繫任何端點的情況下呈現標頭與酬載。

瀏覽器輔助工具的功能範圍刻意設計得很狹窄。它不接受簽署金鑰、不擷取 JWKS 文件,也不執行你應用程式的驗證原則。每次成功解碼都會被標示為未經驗證,這使它適合作為開發階段的檢視步驟,而非用來取代你隨服務部署的驗證程式碼。針對以無程式碼方式讀取的工作流程,請參閱在瀏覽器中無需程式碼解碼 JWT 權杖指南,它從稍有不同的角度使用相同的工具,當你想與不會撰寫 C# 的團隊成員分享唯讀檢視方式時特別實用。

為何解碼絕不等於驗證

無論是在 C#、瀏覽器或命令列中解碼 JWT,都無法證明該權杖是真實的。可讀取的酬載可能是偽造的、已過期的、以你不信任的金鑰所簽署,或是為完全不同的受眾所簽發的。JwtSecurityTokenHandler.ValidateToken 才是執行驗證的呼叫,它需要簽署金鑰、預期的簽發者、預期的受眾以及時鐘偏移容許度才能正確運作。JWT Decoder 永遠不會做出身分驗證決策,並會將每個結果標示為未經驗證,以避免快速的檢視步驟被誤認為安全性檢查。

權杖中即使不包含密碼,也可能帶有個人識別碼、租戶資料、權限範圍或其他敏感的應用程式細節。請避免將正式環境的憑證貼到共用畫面、螢幕擷圖或會上傳你輸入內容的第三方線上解碼器中。若需要驗證權杖,請在擁有安全性決策的服務中使用維護良好的 JWT 程式庫,於該處驗證演算法、金鑰、簽發者、受眾、到期時間以及任何應用程式特定的需求,並在使用完畢後從任何本機輸入欄位中移除該權杖。

如需更深入的探討,請參閱在 Postman 中以 HS256 於本機產生 JWT 權杖