一、訪問令牌的類型
二、JWT令牌
1、什么是JWT令牌
JWT是JSON Web Token的縮寫,即JSON Web令牌,是一種自包含令牌。
JWT的使用場景:
-
一種情況是webapi,類似之前的阿里雲播放憑證的功能
-
另一種情況是多web服務器下實現無狀態分布式身份驗證
JWT官網有一張圖描述了JWT的認證過程:
JWT的作用:
JWT 最重要的作用就是對 token信息的防偽作用
JWT的原理:
一個JWT由三個部分組成:JWT頭、有效載荷、簽名哈希,最后由這三者組合進行base64編碼得到JWT
2、JWT令牌的組成
典型的,一個JWT看起來如下圖:https://jwt.io/
該對象為一個很長的字符串,字符之間通過"."分隔符分為三個子串。
每一個子串表示了一個功能塊,總共有以下三個部分:JWT頭、有效載荷和簽名
JWT頭
JWT頭部分是一個描述JWT元數據的JSON對象,通常如下所示。
{
"alg": "HS256",
"typ": "JWT"
}
在上面的代碼中,alg屬性表示簽名使用的算法,默認為HMAC SHA256(寫為HS256);typ屬性表示令牌的類型,JWT令牌統一寫為JWT。最后,使用Base64URL算法將上述JSON對象轉換為字符串保存。
有效載荷
有效載荷部分,是JWT的主體內容部分,也是一個JSON對象,包含需要傳遞的數據。 JWT指定七個默認字段供選擇。
iss: jwt簽發者
sub: 主題
aud: 接收jwt的一方
exp: jwt的過期時間,這個過期時間必須要大於簽發時間
nbf: 定義在什么時間之前,該jwt都是不可用的.
iat: jwt的簽發時間
jti: jwt的唯一身份標識,主要用來作為一次性token,從而回避重放攻擊。
除以上默認字段外,我們還可以自定義私有字段,如下例:
{
"name": "Helen",
"admin": true,
"avatar": "helen.jpg"
}
請注意,默認情況下JWT是未加密的,任何人都可以解讀其內容,因此不要構建隱私信息字段,存放保密信息,以防止信息泄露。
JSON對象也使用Base64 URL算法轉換為字符串保存。
簽名哈希
簽名哈希部分是對上面兩部分數據簽名,通過指定的算法生成哈希,以確保數據不會被篡改。
首先,需要指定一個密碼(secret)。該密碼僅僅為保存在服務器中,並且不能向用戶公開。然后,使用標頭中指定的簽名算法(默認情況下為HMAC SHA256)根據以下公式生成簽名。
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(claims), secret)
在計算出簽名哈希后,JWT頭,有效載荷和簽名哈希的三個部分組合成一個字符串,每個部分用"."分隔,就構成整個JWT對象。
Base64URL算法
如前所述,JWT頭和有效載荷序列化的算法都用到了Base64URL。該算法和常見Base64算法類似,稍有差別。
作為令牌的JWT可以放在URL中(例如api.example/?token=xxx)。 Base64中用的三個字符是"+","/"和"=",由於在URL中有特殊含義,因此Base64URL中對他們做了替換:"="去掉,"+"用"-"替換,"/"用"_"替換,這就是Base64URL算法。
注意:base64編碼,並不是加密,只是把明文信息變成了不可見的字符串。但是其實只要用一些工具就可以把base64編碼解成明文,所以不要在JWT中放入涉及私密的信息。
3、JWT的用法
客戶端接收服務器返回的JWT,將其存儲在Cookie或localStorage中。
此后,客戶端將在與服務器交互中都會帶JWT。如果將它存儲在Cookie中,就可以自動發送,但是不會跨域,因此一般是將它放入HTTP請求的Header Authorization字段中。
當跨域時,也可以將JWT放置於POST請求的數據主體中。
三、JWT問題和趨勢
1、JWT默認不加密,但可以加密。生成原始令牌后,可以使用該令牌再次對其進行加密。
2、當JWT未加密時,一些私密數據無法通過JWT傳輸。
3、JWT不僅可用於認證,還可用於信息交換。善用JWT有助於減少服務器請求數據庫的次數。
4、JWT的最大缺點是服務器不保存會話狀態,所以在使用期間不可能取消令牌或更改令牌的權限。也就是說,一旦JWT簽發,在有效期內將會一直有效。
5、JWT本身包含認證信息,因此一旦信息泄露,任何人都可以獲得令牌的所有權限。為了減少盜用,JWT的有效期不宜設置太長。對於某些重要操作,用戶在使用時應該每次都進行身份驗證。
6、為了減少盜用和竊取,JWT不建議使用HTTP協議來傳輸代碼,而是使用加密的HTTPS協議進行傳輸。
四、JWT的使用DEMO
4.1、創建Maven項目
1、項目
項目類型:Maven
groupId:com.atguigu
artifactId:jwt
2、基本依賴
<dependencies>
<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.6</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
3、創建Member.java
package com.atguigu.jwt.entity;
@Data
public class Member {
private String id;
private String nickname;
private String avatar;
}
4.2、創建工具類
1、創建JWT工具類
package com.atguigu.jwt.util;
public class JwtUtils {
public static final String SUBJECT = "guli-user";
//秘鑰
public static final String APP_SECRET = "79e7c69681b8270162386e6daa53d1dc";
//過期時間,毫秒,30分鍾
public static final long EXPIRE = 1000 * 60 * 30;
/**
* 生成Jwt令牌
* @return
*/
public static String generateJwt(Member member){
String token = Jwts.builder()
.setHeaderParam("typ", "JWT") //令牌類型
.setHeaderParam("alg", "HS256") //簽名算法
.setSubject(SUBJECT) //令牌主題
.setIssuedAt(new Date()) //簽發時間
.setExpiration(new Date(System.currentTimeMillis() + EXPIRE)) //過期時間
.claim("id", member.getId())
.claim("nickname", member.getNickname())
.claim("avatar", member.getAvatar())
.signWith(SignatureAlgorithm.HS256, APP_SECRET).compact();
return token;
}
/**
* 校驗jwt
* @param jwtToken
* @return
*/
public static Claims checkJwt(String jwtToken){
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
Claims claims = claimsJws.getBody();
return claims;
}
}
2、測試用例
package com.atguigu.jwt;
public class JwtTest {
@Test
public void testGenerateJwt(){
Member member = new Member();
member.setId("10000");
member.setNickname("Helen");
member.setAvatar("1.png");
String jwt = JwtUtils.generateJwt(member);
System.out.println(jwt);
}
@Test
public void testCheckJwt(){
Claims claims = JwtUtils.checkJwt("jwt字符串");
String id = (String)claims.get("id");
String nickname = (String)claims.get("nickname");
String avatar = (String)claims.get("avatar");
System.out.println(id);
System.out.println(nickname);
System.out.println(avatar);
}
}