Java中JWT的使用的詳細(xì)教程
JWT簡(jiǎn)介
JWT全稱(chēng)為Json Web Token
JWT的本質(zhì)就是一個(gè)字符串,它是將用戶(hù)信息保存到一個(gè)Json字符串中,然后進(jìn)行編碼后得到一個(gè)JWT token,并且這個(gè)JWT token帶有簽名信息,接收后可以校驗(yàn)是否被篡改,所以可以用于在各方之間安全地將信息作為Json對(duì)象傳輸。
JWT的認(rèn)證流程如下:
1 首先,前端通過(guò)Web表單將自己的用戶(hù)名和密碼發(fā)送到后端的接口,這個(gè)過(guò)程一般是一個(gè)POST請(qǐng)求。建議的方式是通過(guò)SSL加密的傳輸(HTTPS),從而避免敏感信息被嗅探
2 后端核對(duì)用戶(hù)名和密碼成功后,將包含用戶(hù)信息的數(shù)據(jù)作為JWT的Payload,將其與JWT Header分別進(jìn)行Base64編碼后拼接簽名,形成一個(gè)JWT Token,形成的JWT Token就是一個(gè)如同lll.zzz.xxx的字符串
3 后端將JWT Token字符串作為登錄成功的結(jié)果返回給前端。前端可以將返回的結(jié)果保存在瀏覽器中,退出登錄時(shí)刪除保存的JWT Token即可
4 前端在每次請(qǐng)求時(shí)將JWT Token放入HTTP請(qǐng)求頭中的Authorization屬性中(解決XSS和XSRF問(wèn)題)
5 后端檢查前端傳過(guò)來(lái)的JWT Token,驗(yàn)證其有效性,比如檢查簽名是否正確、是否過(guò)期、token的接收方是否是自己等等
6 驗(yàn)證通過(guò)后,后端解析出JWT Token中包含的用戶(hù)信息,進(jìn)行其他邏輯操作(一般是根據(jù)用戶(hù)信息得到權(quán)限等),返回結(jié)果

JWT結(jié)構(gòu)
JWT由3部分組成:標(biāo)頭(header)、有效載荷(payLoad)、簽名(signature)。
在傳輸?shù)臅r(shí)候,會(huì)將JWT的3部分分別進(jìn)行Base64編碼后用.連接形成最終傳輸?shù)淖址?/p>
Header
JWT頭是一個(gè)描述JWT元數(shù)據(jù)的JSON對(duì)象,alg屬性表示簽名使用的算法,默認(rèn)為HMAC SHA256(寫(xiě)為HS256);typ屬性表示令牌的類(lèi)型,JWT令牌統(tǒng)一寫(xiě)為JWT。最后,使用Base64 URL算法將上述JSON對(duì)象轉(zhuǎn)換為字符串保存
{
"alg": "HS256",
"typ": "JWT"
}PayLoad
有效載荷部分,是JWT的主體內(nèi)容部分,也是一個(gè)JSON對(duì)象,包含需要傳遞的數(shù)據(jù)。 JWT指定七個(gè)默認(rèn)字段供選擇
iss:發(fā)行人 exp:到期時(shí)間 sub:主題 aud:用戶(hù) nbf:在此之前不可用 iat:發(fā)布時(shí)間 jti:JWT ID用于標(biāo)識(shí)該JWT
除以上默認(rèn)字段外,我們還可以自定義私有字段,一般會(huì)把包含用戶(hù)信息的數(shù)據(jù)放到payload中,如下
{
"id": "123",
"name": "cheng"
}Signature
簽名哈希部分是對(duì)上面兩部分?jǐn)?shù)據(jù)簽名,需要使用base64編碼后的header和payload數(shù)據(jù),通過(guò)指定的算法生成哈希,以確保數(shù)據(jù)不會(huì)被篡改。首先,需要指定一個(gè)密鑰(secret)。該密碼僅僅為保存在服務(wù)器中,并且不能向用戶(hù)公開(kāi)。然后,使用header中指定的簽名算法(默認(rèn)情況下為HMAC SHA256)根據(jù)以下公式生成簽名
HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)
在計(jì)算出簽名哈希后,JWT頭,有效載荷和簽名哈希的三個(gè)部分組合成一個(gè)字符串,每個(gè)部分用.分隔,就構(gòu)成整個(gè)JWT對(duì)象

注意JWT每部分的作用,在服務(wù)端接收到客戶(hù)端發(fā)送過(guò)來(lái)的JWT token之后:
header和payload可以直接利用base64解碼出原文,從header中獲取哈希簽名的算法,從payload中獲取有效數(shù)據(jù)
signature由于使用了不可逆的加密算法,無(wú)法解碼出原文,它的作用是校驗(yàn)token有沒(méi)有被篡改。服務(wù)端獲取header中的加密算法之后,利用該算法加上secretKey對(duì)header、payload進(jìn)行加密,比對(duì)加密后的數(shù)據(jù)和客戶(hù)端發(fā)送過(guò)來(lái)的是否一致。
為了完成簽名,除了用到header信息和payload信息外,還需要算法的密鑰,也就是secretKey。加密的算法一般有2類(lèi):
- 對(duì)稱(chēng)加密:secretKey指加密密鑰,可以生成簽名與驗(yàn)簽
- 非對(duì)稱(chēng)加密:secretKey指私鑰,只用來(lái)生成簽名,不能用來(lái)驗(yàn)簽(驗(yàn)簽用的是公鑰)
JWT的密鑰或者密鑰對(duì),一般統(tǒng)一稱(chēng)為JSON Web Key,也就是JWK
到目前為止,jwt的簽名算法有三種: - HMAC【哈希消息驗(yàn)證碼(對(duì)稱(chēng))】:HS256/HS384/HS512
- RSASSA【RSA簽名算法(非對(duì)稱(chēng))】(RS256/RS384/RS512)
- ECDSA【橢圓曲線(xiàn)數(shù)據(jù)簽名算法(非對(duì)稱(chēng))】(ES256/ES384/ES512)
Java中使用JWT
我這里用的是java-jwt
引入依賴(lài)
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.10.3</version>
</dependency>對(duì)稱(chēng)簽名
生成JWT的Token
/**
* 生成JWT token
*/
@Test
void generateToken(){
//預(yù)設(shè)一個(gè)token過(guò)期時(shí)間
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.HOUR,1);//過(guò)期時(shí)間為1小時(shí)
String token = JWT.create()
.withHeader(new HashMap<>())//Header
.withClaim("userId", 123)//PayLoad
.withClaim("userName", "程")
.withClaim("userId1", "456")
.withExpiresAt(calendar.getTime())//過(guò)期時(shí)間
.sign(Algorithm.HMAC256("12345"));//簽名用的密鑰secret
System.out.println(token);
}
利用base64解密工具來(lái)查看生成的JWT Token中header和payLoad的明文


解析JWT字符串
/**
* 解析jwt字符串
*/
@Test
void resolveToken(){
String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VySWRJbnQiOjEyMywidXNlcklkU3RyaW5nIjoiNDU2IiwidXNlck5hbWUiOiLnqIsiLCJleHAiOjE2NzU5MTQzMDZ9.MNC-YCxt8C0t9SNbj3_XMRknkK3-PKBP5xX6_JLB8y8";
//創(chuàng)建解析對(duì)象,使用的算法和secret要和創(chuàng)建token時(shí)保持一致
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("12345")).build();
DecodedJWT decodedJWT = jwtVerifier.verify(token);
System.out.println(decodedJWT.getPayload());//base64編碼的payLoad
Claim userIdInt = decodedJWT.getClaim("userIdInt");
Claim userIdString = decodedJWT.getClaim("userIdString");
Claim userName = decodedJWT.getClaim("userName");
System.out.println("userIdInt:"+userIdInt.asInt());
System.out.println("userIdString:"+userIdString.asString());
System.out.println("userName:"+userName.asString());
System.out.println("過(guò)期時(shí)間:"+new LocalDateTime(decodedJWT.getExpiresAt()));
}
如果我們?cè)O(shè)置的token時(shí)間短,比如設(shè)置為1秒

那么解析時(shí)為拋出如下異常

非對(duì)稱(chēng)簽名
private static final String RSA_PRIVATE_KEY = "...";
private static final String RSA_PUBLIC_KEY = "...";
/**
* 生成token
* @param payload token攜帶的信息
* @return token字符串
*/
public static String getTokenRsa(Map<String,String> payload){
// 指定token過(guò)期時(shí)間為7天
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DATE, 7);
JWTCreator.Builder builder = JWT.create();
// 構(gòu)建payload
payload.forEach((k,v) -> builder.withClaim(k,v));
// 利用hutool創(chuàng)建RSA
RSA rsa = new RSA(RSA_PRIVATE_KEY, null);
// 獲取私鑰
RSAPrivateKey privateKey = (RSAPrivateKey) rsa.getPrivateKey();
// 簽名時(shí)傳入私鑰
String token = builder.withExpiresAt(calendar.getTime()).sign(Algorithm.RSA256(null, privateKey));
return token;
}
/**
* 解析token
* @param token token字符串
* @return 解析后的token
*/
public static DecodedJWT decodeRsa(String token){
// 利用hutool創(chuàng)建RSA
RSA rsa = new RSA(null, RSA_PUBLIC_KEY);
// 獲取RSA公鑰
RSAPublicKey publicKey = (RSAPublicKey) rsa.getPublicKey();
// 驗(yàn)簽時(shí)傳入公鑰
JWTVerifier jwtVerifier = JWT.require(Algorithm.RSA256(publicKey, null)).build();
DecodedJWT decodedJWT = jwtVerifier.verify(token);
return decodedJWT;
}
到此這篇關(guān)于Java中JWT的使用的文章就介紹到這了,更多相關(guān)Java中JWT使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java Jwt庫(kù)的簡(jiǎn)介及使用詳解
- 深入了解java-jwt生成與校驗(yàn)
- Java中的JWT使用詳解
- java使用JWT的方法
- Java如何簡(jiǎn)單快速入門(mén)JWT(token生成與驗(yàn)證)
- 基于Java驗(yàn)證jwt token代碼實(shí)例
- Java如何使用JWT實(shí)現(xiàn)Token認(rèn)證機(jī)制
- 在Java中使用Jwt的示例代碼
- Java?JWT實(shí)現(xiàn)跨域身份驗(yàn)證方法詳解
- 使用Java生成JWT令牌的示例代碼
- Java jwt使用公鑰字符串驗(yàn)證解析token鎖方法詳解
- java-jwt 使用小結(jié)
相關(guān)文章
三分鐘帶你了解SpringBoot真正的啟動(dòng)引導(dǎo)類(lèi)
這篇文章主要介紹了三分鐘帶你了解SpringBoot真正的啟動(dòng)引導(dǎo)類(lèi),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
mybatis的大于小于號(hào)轉(zhuǎn)義符號(hào)一覽
這篇文章主要介紹了mybatis的大于小于號(hào)轉(zhuǎn)義符號(hào)一覽,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08
Java?CompletableFuture實(shí)現(xiàn)多線(xiàn)程異步編排
這篇文章主要為大家介紹了Java?CompletableFuture實(shí)現(xiàn)多線(xiàn)程異步編排,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09
Java實(shí)現(xiàn)餐廳點(diǎn)餐系統(tǒng)的實(shí)例代碼
這篇文章主要介紹了Java實(shí)現(xiàn)餐廳點(diǎn)餐系統(tǒng),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-06-06
gradle構(gòu)建工具和java不兼容錯(cuò)誤的解決方案
這篇文章主要介紹了如何解決AS中Gradle和Java版本不兼容的問(wèn)題,主要解決方案包括升級(jí)Gradle版本或降級(jí)Java版本,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2025-02-02
Java實(shí)現(xiàn)簡(jiǎn)單的飛機(jī)大戰(zhàn)游戲(控制主飛機(jī)篇)
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)簡(jiǎn)單的飛機(jī)大戰(zhàn)游戲,控制主飛機(jī),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05
java 制作驗(yàn)證碼并進(jìn)行驗(yàn)證實(shí)例詳解
這篇文章主要介紹了java 制作驗(yàn)證碼并進(jìn)行驗(yàn)證實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-04-04

