1.什么是JWT?
JWT全稱JSON Web Token。是為了在網絡應用環境鍵傳遞聲明而執行的一種基於JSON的開放標准。
2.JWT的使用場景?
授權:一旦用戶登錄,每個后續請求將包括JWT,允許用戶訪問該令牌允許的路由,服務和資源。單點登錄是一種在廣泛使用JWT的功能,因為它的開銷很小,並且能夠在不同的域中輕松使用。
信息交換:JSON Web令牌是在各方之間安全傳輸信息的好方法。因為JWT可以簽名 - 例如,使用公鑰/私鑰對 - 您可以確定發件人是他們所說的人。此外,由於使用標頭和有效負載計算簽名,您還可以驗證內容是否未被篡改。
3.JWT的結構組成?
由三部分組成,用(.
)進行分隔,他們是
- 頭部(header)
- 有效載荷(payload)
- 簽名(signature)
因此,JWT通常如下表示
xxxxxx.yyyyy.zzzzz
頭部:
JWT頭部分是一個描述JWT元數據的JSON對象,通常如下所示。
在上面的代碼中,alg屬性表示簽名使用的算法,默認為HMAC SHA256(寫為HS256);typ屬性表示令牌的類型,JWT令牌統一寫為JWT。然后,這個json被編碼稱Base64Url,形成JWT的第一部分。
有效載荷:
有效載荷是JWT的主體內容部分,也是一個JSON對象,包含需要傳遞的數據。JWT指定七個默認字段供選擇。
iss:發行人
exp:到期時間
sup:主題
aud:用戶
nbf:在此之前不可用
iat:發行時間
jti:JWT ID用於標識該JWT
除以上默認字段外,我們還可以自定義私有字段,如下例:
請注意,默認情況下JWT是未加密的,任何人都可以解讀其內容,因此不要構建隱私信息字段,存放保密信息,以防止信息泄露。
JSON對象也使用Base64 URL算法轉換為字符串保存,形成JWT第二部分。
簽名:
簽名是對上面兩部分數據簽名,通過指定的算法生成哈希,以確保數據不會被篡改。
首先,需要制定一個密鑰(secret)。該密鑰僅僅為保存在服務器中,並且不能向用戶公開。然后,使用頭部中指定的簽名算法(默認情況下為HMACSHA256)根據以下公式生成簽名。
在計算出簽名后,將JWT頭部,有效載荷和簽名的三個部分組合成一個字符串,每個部分用"."分隔,就構成整個JWT對象,返回給用戶。
4.JWT的用法?
JWT的原理是,服務器認證以后,生成一個JSON對象,發回給用戶,如下面格式
{
"姓名": "張三",
"角色": "管理員",
"到期時間": "2018年7月1日0點0分"
}
以后用戶與服務端通信的時候,都要返回這個JSON對象。服務器完全只靠這個對象認定用戶身份。為了防止用戶篡改數據,服務器在生成這個對象的時候,會加上簽名。
客戶端收到服務器返回的JWT,可以存儲到Cookie里面,也可以存儲到localStorage。
此后,客戶端每次與服務器通信,都要帶上這個JWT。你可以把它放在cookie里面自動發送,但是這樣就不能實現跨域。所以更好的做法是放在HTTP請求的頭信息Authorization字段里面
另一種做法是,跨域的時候,JWT就放在POST請求的數據里面。
5.JWT特點?
(1)JWT默認是不加密的,但也是可以加密的。生成原始Token以后,可以用密鑰在加密一次。
(2)JWT不加密的情況下,不能將秘密數據寫入JWT。
(3)JWT不僅可以用於認證,也可以死用於交換信息。有效使用JWT,可以降低服務器的查詢數據庫的次數。
(4)JWT最大的缺點是,用於服務器不保存sessi000on狀態,因此無法在使用過程中廢止某個token,或者更改token的權限,也就是說,一旦JWT簽發了,在到期之前就會始終有效,除非服務器部署額外的邏輯。
(5)JWT本身包含了認證信息,一旦泄露,任何人都可以獲得該令牌的所有權限。為了減少盜用,JWT的有效期限應該設置的比較短。對於一些比較重要的權限,使用時應該再次對用戶進行認證。
(6)為了較少盜用,JWT不應該使用HTTP協議明碼傳輸,要使用HTTPS協議傳輸。
7.JWT的入門案例(Java方式)
創建一個項目,先引入依賴。
創建一個Jwtutil工具類,里面有包括創建Jwt和解析Jwt
創建JWT
public String createJWT(String id, String subject, long ttlMillis) throws Exception {
//指定簽名的時候使用的簽名算法,也就是header那部分,jjwt已經將這部分內容封裝好了
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
//生成JWT的時間
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
//創建payload的私有聲明(根據特定的業務需要添加,如果要拿這個做驗證,一般是需要和jwt的接收方提前溝通好驗證方式的)
Map<String,Object> claims = new HashMap<String,Object>();
claims.put("uid", "DSSFAWDWADAS...");
claims.put("user_name", "admin");
claims.put("nick_name","DASDA121");
/**
* 生成簽名的時候使用的秘鑰secret,這個方法本地封裝了的,一般可以從本地配置文件中讀取,切記這個秘鑰不能外露哦。它就是你服務端的私鑰,在任何場景都不應該流露出去。一旦客戶端得知這個secret, 那就意味着客戶端是可以自我簽發jwt了。
*/
SecretKey key = generalKey();
//下面就是在為payload添加各種標准聲明和私有聲明了
//這里其實就是new一個JwtBuilder,設置jwt的body
JwtBuilder builder = Jwts.builder()
//如果有私有聲明,一定要先設置這個自己創建的私有的聲明,這個是給builder的claim賦值,一旦寫在標准的聲明賦值之后,就是覆蓋了那些標准的聲明的
.setClaims(claims)
//設置jti(JWT ID):是JWT的唯一標識,根據業務需要,這個可以設置為一個不重復的值,主要用來作為一次性token,從而回避重放攻擊。
.setId(id)
//iat: jwt的簽發時間
.setIssuedAt(now)
//sub(Subject):代表這個JWT的主體,即它的所有人,這個是一個json格式的字符串,可以存放什么userid,roldid之類的,作為什么用戶的唯一標志。
.setSubject(subject)
//設置簽名使用的簽名算法和簽名使用的秘鑰
.signWith(signatureAlgorithm, key);
if (ttlMillis >= 0) {
long expMillis = nowMillis + ttlMillis;
Date exp = new Date(expMillis);
//設置過期時間
builder.setExpiration(exp);
}
//就開始壓縮為xxxxxxxxxxxxxx.xxxxxxxxxxxxxxx.xxxxxxxxxxxxx這樣的jwt
return builder.compact();
}
解析JWT
public Claims parseJWT(String jwt) throws Exception{
//簽名秘鑰,和生成的簽名的秘鑰一模一樣
SecretKey key = generalKey();
//得到DefaultJwtParser
Claims claims = Jwts.parser()
//設置簽名的秘鑰
.setSigningKey(key)
//設置需要解析的jwt
.parseClaimsJws(jwt).getBody();
//claims相當於一個map,包含了我們想要的信息
return claims;
}
接下來測試一下,首先創建一個jwt,得到一個xxxxxx.zzzzzz.yyyyy字符串
我們再來解析一下這個字符串:
這樣,我們就能夠根據這個字符串解析出用戶信息,從而驗證登錄授權等功能。