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定義的聲明,例如
iss
、sub
、aud
等。 - 自訂聲明 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-jwt
、jose4j
。
文章有幫助的話還幫忙點個Google廣告,感恩。
沒有留言:
張貼留言