JSON Web Token(縮寫 JWT)是目前最流行的跨域認(rèn)證解決方案。
1. 一般的認(rèn)證流程
- 用戶向服務(wù)器發(fā)送用戶名和密碼
- 服務(wù)器通過驗證后,在當(dāng)前對話(session)里面保存相關(guān)數(shù)據(jù),比如用戶角色、登錄時間等等
- 服務(wù)端向用戶返回一個session_id,寫入用戶的Cookie
- 用戶隨后的每一次請求,都會通過Cookie,將session_id傳回服務(wù)器
- 服務(wù)器收到session_id,找到前期保存的數(shù)據(jù),由此得知用戶的身份
????這種模式在單機(jī)情況下沒有問題。如果是服務(wù)集群,或者是跨域的服務(wù)導(dǎo)向架構(gòu),就要求session數(shù)據(jù)共享,每臺服務(wù)器都能都讀取到session。例如:A 網(wǎng)站和 B 網(wǎng)站是同一家公司的關(guān)聯(lián)服務(wù)。要求用戶只要在其中一個網(wǎng)站登錄,再訪問另一個網(wǎng)站就自動登錄。
????一種解決方案就是:服務(wù)器索性不保存 session數(shù)據(jù)了,所有數(shù)據(jù)都保存在客戶端,每次請求都發(fā)回服務(wù)器。JWT 就是這種方案的一個代表。
2. JWT原理
- 服務(wù)器認(rèn)證以后,生成一個JSON對象,發(fā)回給用戶
- 之后用戶與服務(wù)端通信的時候,都要發(fā)回這個 JSON對象
- 服務(wù)器完全只靠這個對象認(rèn)定用戶身份
- 為了防止用戶篡改數(shù)據(jù),服務(wù)器在生成這個對象的時候,會加上簽名
- 這樣服務(wù)器就不保存任何session數(shù)據(jù)了,也就是說,服務(wù)器變成無狀態(tài)了,從而比較容易實現(xiàn)擴(kuò)展
3. JWT例子
- 它是一個很長的字符串,中間用點 . 分隔成三個部分
- 包括了頭部Header、負(fù)載Payload、簽名Signature。即Header.Payload.Signature
- 以下是一個例子,可在JWT解析網(wǎng)址下解析并得到信息
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
3.1. Header
- 一個JSON對象,描述JWT的元數(shù)據(jù) ,格式是:{"alg": "HS256","typ": "JWT"}
- alg屬性表示簽名的算法(algorithm),默認(rèn)是HMAC SHA256(HS256)
- typ屬性表示這個令牌的類型,JWT令牌統(tǒng)一寫為JWT
- 最后,將上面的 JSON 對象使用Base64URL算法轉(zhuǎn)成字符串
3.2. Payload
- 也是一個JSON對象,用來存放實際需要傳遞的數(shù)據(jù)
- JWT 規(guī)定了7個官方字段,供選用:
字段 解釋 備注 iss issuer 簽發(fā)人 exp expiration time 過期時間 sub subject 主題 aud audience 受眾 nbf Not Before 生效時間 iat Issued At 簽發(fā)時間 jti JWT ID 編號 - 除了官方字段,還能自定義私有字段{"sub":"1234567890","name": "John Doe","admin": true}
- 注意,JWT默認(rèn)是不加密的,任何人都可以讀到,所以不要把秘密信息放在這個部分
- 這個JSON對象也要使用Base64URL算法轉(zhuǎn)成字符串
3.3. Signature
- 是對Header、Payload這兩部分的簽名,防止數(shù)據(jù)篡改
- 首先,需要指定一個密鑰(secret)。這個密鑰只有服務(wù)器才知道,不能泄露給用戶
- 然后,使用Header里面指定的簽名算法(默認(rèn)是HMAC SHA256),按照公式產(chǎn)生簽名
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret )
3.4. Base64URL
- 前面提到的Header 和 Payload串型化的算法是 Base64URL
- 這個算法跟 Base64 算法基本類似,但有一些小的不同
- JWT 作為一個令牌,有些場合可能會放到URL(如 api.example.com/?token=xxx)
- Base64 有三個字符+、/和=,在 URL里面有特殊含義
- 所以要被替換掉,= 被省略 + 替換成 - / 替換成 _ ,這就是 Base64URL 算法
4. JWT的使用
- 客戶端收到服務(wù)器返回的 JWT,可以儲存在 Cookie或localStorage
- 此后,客戶端每次與服務(wù)器通信,都要帶上這個JWT
- 可以放在Cookie里面自動發(fā)送,但是這樣不能跨域
- 所以更好的做法是放在 HTTP請求的頭信息Authorization字段里面Authorization: Bearer <token>
- 另一種做法是跨域的時候,JWT 就放在 POST 請求的數(shù)據(jù)體里面
5. JWT特點
- 默認(rèn)不加密,但可以在生成原始Token之后,再用密鑰加密一次
- 不加密的情況下,不能將秘密數(shù)據(jù)寫到JWT
- JWT不僅可以用于認(rèn)證,也可以用于交換信息
- 有效使用JWT,可以降低服務(wù)器查詢數(shù)據(jù)庫的次數(shù)
- 由于服務(wù)器不保存session狀態(tài),因此無法在使用過程中廢止某個token,或者更改token權(quán)限
- 即JWT一旦簽發(fā),在到期之前就會始終有效,除非服務(wù)器部署額外的邏輯(如Redis)
- JWT本身包含了認(rèn)證信息,一旦泄露,任何人都可以獲得該令牌的所有權(quán)限
- 為了減少盜用,JWT的有效期應(yīng)該設(shè)置得比較短
- 重要的權(quán)限,應(yīng)該在使用時再次對用戶做認(rèn)證
- 為了減少盜用,JWT不應(yīng)該使用HTTP協(xié)議明碼傳輸,要使用HTTPS協(xié)議傳輸