基於JWT(JSON Web Token)的token身份驗證
阿新 • • 發佈:2020-06-24
相逢便是緣,路過點個贊 ^.^
原始碼:https://github.com/yulc-coding/java-note/tree/master/jwt
介紹
JWT是一種用於通訊雙方之間傳遞安全資訊的簡潔的、URL安全的表述性宣告規範,經常用在跨域身份驗證。因為存在數字簽名,因此可以起到防串改的作用
傳統session模式
相對於傳統的session認證,通常將session儲存在服務端,需要伺服器去維護。並且在伺服器叢集或請求服務跨域的情況下,需要共享session,使每臺伺服器都能讀取session,比如將session持久化,增加了開銷。
jwt token模式
使用者登入後伺服器將相關資料生成一個token返回客戶端
客戶端每次發起請求帶上token
伺服器獲取token後校驗token驗證合法性
格式
- Header 頭資訊
{
"alg": "Algorithm 加密方法:HS256",
"cty": "Content Type ",45); word-wrap: inherit !important; word-break: inherit !important;" class="hljs-attr">"typ": "Type" ,45); word-wrap: inherit !important; word-break: inherit !important;" class="hljs-attr">"kid": "Key Id"
}
複製程式碼
- Payload 載體資訊:資料包放在這裡
"iss": "Issuer JWT的簽發者",45); word-wrap: inherit !important; word-break: inherit !important;" class="hljs-attr">"aud": "Audience 接收JWT的一方",45); word-wrap: inherit !important; word-break: inherit !important;" class="hljs-attr">"sub": "Subject JWT的主題",45); word-wrap: inherit !important; word-break: inherit !important;" class="hljs-attr"> "exp": "Expiration Time JWT的過期時間",45); word-wrap: inherit !important; word-break: inherit !important;" class="hljs-attr">"nbf": "Not Before 在xxx之間,該JWT都是可用的",45); word-wrap: inherit !important; word-break: inherit !important;" class="hljs-attr">"iat": "Issued At 該JWT簽發的時間",45); word-wrap: inherit !important; word-break: inherit !important;" class="hljs-attr">"jti": "JWT ID JWT的唯一身份標識",45); word-wrap: inherit !important; word-break: inherit !important;" class="hljs-attr">"xxx": "自定義屬性"
}
複製程式碼
Signature 簽名資訊 = 加密演演算法(header + "." + payload,金鑰)
TOKEN
base64(Header).base64(Payload).Signature
複製程式碼
程式碼
pom
<!-- JWT 支援-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.8.3</version>
</dependency>
<!-- 很好用的一個工具類包 這裡用來處理json和AES加密-->
<groupId>cn.hutool</artifactId>hutool-all</version>5.0.3</dependency>
複製程式碼
建立token
可以傳入公有宣告的欄位,也可以傳入自定義的欄位
/**
* 建立token
*
* @param json 需要放入token的引數,多個引數可以封裝成json或者map
* @return token
*/
public static String createToken(JSONObject json) {
try {
// 加密方式
Algorithm algorithm = Algorithm.HMAC256(SECRET);
return JWT.create()
.withSubject(json.toString())
.withIssuer("ylc")
// 設定過期時間為1分鐘後
.withExpiresAt(DateUtil.offsetMinute(new Date(), 1))
.withClaim("customString", "自定義引數")
.withArrayClaim("customArray", new Integer[]{1,250); word-wrap: inherit !important; word-break: inherit !important;" class="hljs-number">2,250); word-wrap: inherit !important; word-break: inherit !important;" class="hljs-number">3})
.sign(algorithm);
} catch (JWTCreationException exception) {
//Invalid Signing configuration / Couldn't convert Claims.
System.out.println(exception.getMessage());
return null;
}
}
複製程式碼
token校驗
包含:
格式校驗:header.payload.signature
加密方式校驗: Header中的alg值
簽名資訊Signature校驗,防止資料被篡改
載體Payload 中公有宣告欄位校驗,如iss,jti,exp過期時間的校驗
/**
* 校驗token 合法性
*
* @param token to verify.
*/
static boolean verifyToke(String token) {
try {
Algorithm algorithm = Algorithm.HMAC256(SECRET);
JWTVerifier verifier = JWT.require(algorithm)
// 驗證簽發人是否相同
.withIssuer("ylc")
.build();
/*
* 校驗:
* 格式校驗:header.payload.signature
* 加密方式校驗 Header中的alg
* 簽名資訊校驗,防串改
* 載體Payload 中公有宣告欄位校驗
*/
verifier.verify(token);
true;
} catch (JWTVerificationException //Invalid signature/claims
System.out.println(false;
}
}
複製程式碼
解析token
可以通過jwt.getClaims() 獲取所有宣告欄位
也可以通過 jwt.getClaim(name) 獲取指定名稱的宣告欄位
/**
* 解析token
*
* @param token to decode.
*/
static void decodeToken(String token) {
try {
DecodedJWT jwt = JWT.decode(token);
Map<String, Claim> claims = jwt.getClaims();
Claim customStringClaim = claims.get("customString");
Claim customArrayClaim = claims."customArray");
String issuer = jwt.getIssuer();
String subject = jwt.getSubject();
System.out.println(customStringClaim.asString());
System.out.println(Arrays.toString(customArrayClaim.asArray(Integer.class)));
System.out.println(issuer);
System.out.println(JSONUtil.parseObj(subject));
} catch (JWTDecodeException exception) {
//Invalid token
System.out.println(exception.getMessage());
}
}
複製程式碼
缺點
- 預設生成的token不加密,別人可以解析token獲取到其中的資料,如果要傳遞敏感資訊,可以先將資訊加密後再放入token,或者將生成的token進行加密
- 每次延長token有效期,會從新生成一個token,需要前端替換原有的token
- 由於伺服器不儲存 session狀態,因此無法在使用過程中廢止某個token,或者更改 token的許可權。也就是說,一旦JWT簽發了,在到期之前就會始終有效,需要在服務端設定相應的業務邏輯去處理。