網頁

2017/11/7

JWT (JSON Web Tokens)

JWT是JSON Web Token的簡稱,JWT不是程式也不是函式庫,而是定義如何在兩個網路實體間驗證身分的一種訊息格式。JWT詳細規範參考RFC 7519

引述官網說明:

JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties. JWT.IO allows you to decode, verify and generate JWT.


JWT使用JSON來傳遞資料,傳遞的資料會有簽名(Signature),因此資料可以被驗證(Authentication)。


JWT的結構

JWT的由下面三個部分組成,每一個部分用點號.分開。

  • 標頭 Header
  • 酬載 Payload
  • 簽名 Signature

所以JWT的樣子是xxxxx.yyyyy.zzzzz


標頭 Header

又稱JOSE Header,包含JWT的簽章及加密資訊,例如加密類型alg及類型typ。例如:

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

然後以Base64Url編碼(例如 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9),此為JWT的header部分。


酬載 Payload

Payload存放要求資料的聲明(claims)(即JWT物件的成員),例如使用者名稱等。claims分為下面幾種:

  • 註冊聲明 registered claims - JWT定義的聲明,例如isssubaud等。
  • 自訂聲明 custom claims - 應用程式自訂的聲明,分為public claims及private claims。
    • 公開聲明 public claims - 可被其他JWT使用者利用的聲明。為了避免命名衝突,公開聲明應在IANA JSON Web Token Claims Registry註冊或可防止命名衝突,例如前綴命名空間。
    • 私有聲明 private claims - 僅用於應用程式本身的聲明的非公開聲明。

Payload的json樣子如下。sub為registerd claim;name為public claim;admin為private claim。

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

與header一樣,payload的json也會以Base64Url編碼(例如 eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9


簽名 Signature

Signature簽名用來驗證JWT的發送者的身分,確保資訊沒有被修改,是以編碼過的header,payload透過密鑰(secret)及加密算法(例如HMAC SHA256)產生。簽名以下面方式來建立,第一部分為header.payload的Base64Url編碼,第二部分為secret(這邊secret為"secret")。

HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), "secret")

產生的簽名為TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

 ┌────────────────┐          ┌────────┐
 │ header.payload │          │ secret │
 └────────┬───────┘          └───┬────┘
          │                      │
          │   ┌──────────────┐   │
          └──►│  HMACSHA256  │◄──┘
              └───────┬──────┘
                      │
                      ▼
┌─────────────────────────────────────────────┐
│                 signature                   │
│ TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ │
└─────────────────────────────────────────────┘

將header、payload及signature組合起來就是JWT的token。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

注意JWT只有經過編碼(encoded)但並沒有加密(encrypted),所以JWT的內容只要解碼都看得到,因此不要存放敏感資訊例如密碼或信用卡號等在JWT中。


JWT的運作方式

當使用者成功登入後,server端會回傳JWT給client端並保存(例如存在localStorage或sessionStorage或cookies),下次再請求被保護的資源時,必須將JWT放在request header的Authorization中並以Bearer作為schema來傳送給server,例如

Authorization: Bearer <token>

Server端收到token後使用原簽名來解析並驗證發送者的身分。

         ┌──────────┐                               ┌──────────┐
         │  Client  │                               │  Server  │
         │ (Browser)│                               │          │
         └─────┬────┘                               └─────┬────┘
               │                                          │

               │ POST /login with username and password   │
               ├────────────────────────────────────────►┌┤
               │                                         ││  verify username and password
                                                         ││
               │                                         ││
               │                                         ││  create JWT token with secret
                                                         ││    create signature
               │                                         ││     ┌───────────────────────────────────┐
               │                                         ││     │ HMACSHA256(                       │
                                                         ││     │   base64UrlEncode(header) + "." + │
               │                                         ││     │   base64UrlEncode(payload),       │
               │                                         ││     │   "secret"                        │
                                                         ││     │ )                                 │
               │       return JWT token to Client        ││     └───────────────────────────────────┘
              ┌┤◄── ── ── ── ── ── ── ── ── ── ── ── ── ─┴┤
              ││                                          │
save JWT token││
              ││      sends request with JWT token        │
              └┼────────────────────────────────────────►┌┤  create signature from header and payload
               │                                         ││    of received JWT token and compare with
               │                                         ││    the signature of received JWT token
                                                         ││
               │                response                 ││
               │◄── ── ── ── ── ── ── ── ── ── ── ── ── ─┴┤
               │                                          │

               │                                          │
               ▼                                          ▼

JWT機制是無狀態(stateless),即不用session保存使用者狀態,server端不會紀錄你是否曾經來過的資訊。Clinet請求時必須提供token;而傳統session的驗證機制是有狀態(stateful),即server端會紀錄使用者的狀態,又session id是存放在cookie中,發送請求時cookie會自動隨著request發送,所以會有CSRF(Cross-site request forgery)的風險。

JWT另一特色是本身即可存放被請求的資訊在payload,這樣就可以減少對資料庫的連線請求。例如傳統使用token驗證時,server端通常會將token對照的使用者資訊存在資料庫中,所以必須拿token至資料庫比對查詢出使用者資訊後再回傳給client;而JWT的做法是payload本身即可裝載使用者資訊,client將JWT傳至server端驗證過後,即可將JWT解碼並取得payload並返回給使用者。

也因此JWT在不同網路實體間透過APIs傳遞資訊時的驗證非常有用,尤其是微服務間的身份驗證,使得CORS(跨來源資源共用)不再是個問題,因為JWT不使用cookies驗證。

在官網有提供各種語言的JWT函式庫,例如Java的java-jwtjose4j

文章有幫助的話還幫忙點個Google廣告,感恩。



沒有留言:

張貼留言