JWT 的組成結構:Header、Payload、Signature 完整解析

Published December 18, 2025 by 徐培鈞
基礎概念

上一篇文章中,我們介紹了 JWT 的運作原理:為什麼需要 JWT、簽名機制如何防止偽造、以及完整的登入流程。

這篇文章,我們要深入探討 JWT 的內部結構。

回顧:JWT 的三個部分

JWT 是一個用「.」分隔的字串,由三個部分組成:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxMjMsInVzZXJuYW1lIjoiam9obiJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

拆開來看:

  • Header(標頭):描述這個 Token 的「元資料」
  • Payload(內容):實際要傳遞的「資料」
  • Signature(簽名):用來驗證 Token 有沒有被竄改

接下來,我們逐一深入介紹。

Header(標頭)

Header 用來描述這個 JWT 的「元資料」,也就是「這個 Token 是怎麼產生的」。

Header 的內容

一個標準的 Header 長這樣:

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

alg(Algorithm)

alg 指的是「簽名演算法」,告訴伺服器:「這個 JWT 的簽名是用什麼演算法計算出來的」。

什麼是簽名演算法?你可以把它想成一套「計算公式」。把資料和密鑰丟進去,它會算出一個結果(簽名)。不同的演算法,就是不同的計算公式,算出來的結果也會不一樣。

常見的演算法有:

全名HMAC-SHA256
類型對稱式
全名HMAC-SHA384
類型對稱式
全名HMAC-SHA512
類型對稱式
全名RSA-SHA256
類型非對稱式
全名RSA-SHA384
類型非對稱式
全名RSA-SHA512
類型非對稱式
全名ECDSA-SHA256
類型非對稱式

關於對稱式和非對稱式的差別,我們後面會詳細說明。

typ(Type)

typ 指的是「Token 類型」,對於 JWT 來說,這個值就是 JWT

你可能會想:「這不是廢話嗎?JWT 的類型當然是 JWT。」

沒錯,這個欄位看起來有點多餘。它存在的原因是:JWT 的格式(Header.Payload.Signature)也被其他標準採用,例如 JWS(JSON Web Signature)、JWE(JSON Web Encryption)等。typ 欄位可以幫助接收方快速判斷這是哪一種 Token。

但在實務上,這個欄位是可選的,大部分情況下看到的就是 JWT,不寫也沒關係。

Header 的編碼

Header 是一個 JSON 物件,會經過 Base64Url 編碼,變成 JWT 的第一段。

什麼是 Base64?它是一種把資料轉換成「純文字」的編碼方式。因為 JSON 裡面可能有特殊字元(像是 {":),直接放在網址或 HTTP Header 裡可能會出問題,所以先用 Base64 轉換成只有英文字母、數字和少數符號的字串,方便傳輸。

Base64Url 是 Base64 的變體,把 + 換成 -/ 換成 _,讓它可以安全地放在網址裡。

原始 JSON:{"alg":"HS256","typ":"JWT"}

     Base64Url 編碼
    
編碼結果:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

Payload(內容)

Payload 是 JWT 的「主體」,存放實際要傳遞的資料。

Payload 的內容

一個 Payload 可能長這樣:

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

Payload 裡的每一個欄位,我們稱為 Claim(聲明)

標準 Claims

JWT 規範定義了一些「標準 Claims」,這些是大家約定俗成的欄位名稱:

全名Issuer
說明簽發者,誰簽發了這個 JWT
全名Subject
說明主體,這個 JWT 是關於誰的
全名Audience
說明接收者,這個 JWT 是給誰用的
全名Expiration Time
說明過期時間,Unix 時間戳
全名Not Before
說明生效時間,在此之前 JWT 無效
全名Issued At
說明簽發時間,JWT 是什麼時候簽發的
全名JWT ID
說明JWT 的唯一識別碼

exp(過期時間)

這是最常用的 Claim。exp 是一個 Unix 時間戳,表示這個 JWT 什麼時候會過期。

{
  "exp": 1702900000
}

伺服器驗證 JWT 時,會檢查 exp 是否已經超過現在的時間。如果超過了,就拒絕這個 JWT。

iat(簽發時間)

iat 記錄這個 JWT 是什麼時候簽發的,可以用來計算 JWT 的「年齡」。

{
  "iat": 1702896400
}

自訂 Claims

除了標準 Claims,你也可以放自己的資料:

{
  "user_id": 123,
  "username": "john",
  "role": "admin",
  "permissions": ["read", "write", "delete"]
}

這些自訂的欄位,可以放任何你需要的資料。

Payload 的編碼

跟 Header 一樣,Payload 也是經過 Base64Url 編碼

原始 JSON:{"user_id":123,"username":"john"}

     Base64Url 編碼
    
編碼結果:eyJ1c2VyX2lkIjoxMjMsInVzZXJuYW1lIjoiam9obiJ9

⚠️ 重要:Payload 不能放機密資訊

這是 JWT 最重要的安全觀念,請務必記住:

Payload 裡絕對不能放機密資訊!

為什麼?因為 Base64 是「編碼」,不是「加密」。

編碼 vs 加密

編碼(Encoding)轉換格式,方便傳輸
加密(Encryption)保護資料,防止被看到
編碼(Encoding)任何人都能還原
加密(Encryption)只有有金鑰的人能還原
編碼(Encoding)Base64、URL Encoding
加密(Encryption)AES、RSA

Base64 只是把資料轉換成另一種格式,任何人都可以輕易解碼回來

實際示範

假設有人拿到了你的 JWT:

eyJ1c2VyX2lkIjoxMjMsInBhc3N3b3JkIjoibXlzZWNyZXQxMjMifQ

他只需要做 Base64 解碼:

atob('eyJ1c2VyX2lkIjoxMjMsInBhc3N3b3JkIjoibXlzZWNyZXQxMjMifQ')

// 結果:{"user_id":123,"password":"mysecret123"}

密碼就這樣被看光了!

不能放的資料

  • ❌ 密碼
  • ❌ 信用卡號
  • ❌ 身分證字號
  • ❌ 任何機密資訊

可以放的資料

  • ✅ 使用者 ID
  • ✅ 使用者名稱
  • ✅ 角色(admin、member)
  • ✅ 權限列表
  • ✅ 過期時間

簡單來說:放你不介意被別人看到的資料

線上解碼工具

你可以到 jwt.io 這個網站,把任何 JWT 貼上去,它會直接顯示 Header 和 Payload 的內容。

這也證明了:JWT 的內容對任何人都是「透明」的。

Signature(簽名)

Signature 是 JWT 的「防偽機制」,用來驗證 Token 有沒有被竄改。

簽名的計算方式

簽名是這樣計算出來的:

Signature = 演算法(
    Base64Url(Header) + "." + Base64Url(Payload),
    Secret
)

以 HS256 為例:

Signature = HMAC-SHA256(
    "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxMjN9",
    "my-secret-key"
)

為什麼簽名能防止竄改?

因為簽名的計算需要 Secret(密鑰),而這個密鑰只有伺服器知道。

如果有人想要竄改 Payload(例如把 user_id 從 123 改成 456):

  1. 他改了 Payload
  2. 但他不知道 Secret,算不出正確的新簽名
  3. 他只能帶著舊的簽名
  4. 伺服器重新計算簽名,發現對不上
  5. 驗證失敗,拒絕請求
sequenceDiagram
    participant A as 攻擊者
    participant S as 伺服器
    
    Note over A: 修改 Payload,但算不出新簽名
    A->>S: 發送竄改的 JWT(帶著舊簽名)
    Note over S: 重新計算簽名,發現不一致
    S->>A: ❌ 401 Unauthorized

簽名演算法:對稱式 vs 非對稱式

JWT 支援兩種類型的簽名演算法。

對稱式演算法(HMAC)

對稱式演算法使用同一把密鑰來簽名和驗證。

簽名時:把「資料」和「Secret」一起做雜湊運算,產生簽名。

驗證時:把「資料」和「Secret」重新做雜湊運算,產生新的簽名,然後比對兩個簽名是否一致。

flowchart LR
    subgraph 簽名過程
        D1[資料] --> H1[雜湊]
        S1[Secret] --> H1
        H1 --> SIG[簽名]
    end
    
    subgraph 驗證過程
        D2[資料] --> H2[雜湊]
        S2[Secret] --> H2
        H2 --> SIG2[新簽名]
        SIG2 --> C[比對]
        SIG --> C
    end

因為雜湊是單向的、不可逆的,所以簽名和驗證都必須用同一把 Secret 才能算出一樣的結果。

特點

  • 簽名和驗證都用同一把 Secret
  • 只有雜湊,沒有加密
  • 速度快
  • 適合單一服務或內部系統

常見演算法:HS256、HS384、HS512

非對稱式演算法(RSA、ECDSA)

對稱式有一個問題:每個需要驗證 JWT 的服務,都必須知道 Secret。

假設你有 10 個服務都需要驗證 JWT,那這 10 個服務都要知道 Secret。知道 Secret 的服務越多,Secret 外洩的風險就越高。而且一旦 Secret 外洩,攻擊者就能自己產生 JWT,偽造任何人的身份。

非對稱式演算法就是為了解決這個問題。

它使用兩把不同的金鑰

  • Private Key(私鑰):用來產生 JWT 的簽名。只有「認證伺服器」知道,絕對不能外洩。
  • Public Key(公鑰):用來驗證 JWT。可以公開給任何需要驗證的服務。

這兩把金鑰是「配對」的。這是什麼意思?

當你要使用非對稱式演算法時,會先用程式產生一對金鑰。這個產生過程用了特殊的數學公式,讓 Private Key 和 Public Key 之間有一種數學上的對應關係。

跟對稱式不同,非對稱式的簽名過程是「雜湊 + 加密」:

簽名時(產生 JWT)

  1. 對資料做「雜湊」→ 產生雜湊值(這步是單向的,不可逆)
  2. 用 Private Key「加密」這個雜湊值 → 這就是簽名(這步是可逆的)

因為只有 Private Key 能「加密」產生簽名,所以只有擁有 Private Key 的認證伺服器能產生有效的 JWT。

驗證時(驗證 JWT)

  1. 用 Public Key「解密」簽名 → 拿回原本的雜湊值
  2. 對資料重新做「雜湊」→ 產生新的雜湊值
  3. 比對兩個雜湊值是否一致

因為 Public Key 只能「解密」,不能「加密」,所以擁有 Public Key 的服務只能「驗證」JWT,無法偽造簽名。

這樣有什麼好處?

  1. Private Key 只有一個地方知道:只有負責產生 JWT 的認證伺服器需要知道
  2. Public Key 可以隨便給:即使被別人拿到也沒關係,因為它只能驗證,不能產生有效的 JWT
  3. 外洩風險降低:其他服務只有 Public Key,就算被入侵,攻擊者也無法偽造 JWT

常見演算法:RS256、RS384、RS512、ES256

適合的情境:有多個服務需要驗證 JWT、或是需要讓第三方驗證 JWT 的時候。

如何選擇?

建議演算法HS256(對稱式)
建議演算法RS256(非對稱式)
建議演算法RS256(非對稱式)
建議演算法HS256(對稱式)

完整的 JWT 結構圖

flowchart TB
    subgraph Header
        H1[alg: HS256]
        H2[typ: JWT]
    end
    
    subgraph Payload
        P1[sub: 1234567890]
        P2[name: John]
        P3[exp: 1702900000]
    end
    
    subgraph Signature
        S1[HMAC-SHA256]
    end
    
    Header -->|Base64Url| E1[eyJhbGci...]
    Payload -->|Base64Url| E2[eyJzdWIi...]
    
    E1 --> |\+| C[合併]
    E2 --> |\+| C
    C -->|\+ Secret| S1
    S1 --> E3[SflKxwRJ...]
    
    E1 --> J[JWT]
    E2 --> J
    E3 --> J

小結

現在你知道了:

  1. Header 描述 JWT 的元資料(演算法、類型)
  2. Payload 存放實際資料(Claims)
  3. Signature 用來驗證 JWT 沒有被竄改
  4. Base64 是編碼,不是加密,任何人都能解碼 Payload
  5. 絕對不要在 Payload 放機密資訊(密碼、信用卡號等)
  6. 對稱式演算法(HS256)用同一把密鑰簽名和驗證
  7. 非對稱式演算法(RS256)用私鑰簽名、公鑰驗證