SpringBoot JWT令牌的使用
介紹
JWT全稱:JSON Web Token (官網(wǎng):https://jwt.io/)
定義了一種簡(jiǎn)潔的、自包含的格式,用于在通信雙方以json數(shù)據(jù)格式安全的傳輸信息。由于數(shù)字簽名的存在,這些信息是可靠的。
- 簡(jiǎn)潔:是指jwt就是一個(gè)簡(jiǎn)單的字符串??梢栽谡?qǐng)求參數(shù)或者是請(qǐng)求頭當(dāng)中直接傳遞。
- 自包含:指的是jwt令牌,看似是一個(gè)隨機(jī)的字符串,但是我們是可以根據(jù)自身的需求在jwt令牌中存儲(chǔ)自定義的數(shù)據(jù)內(nèi)容。如:可以直接在jwt令牌中存儲(chǔ)用戶的相關(guān)信息。
- 簡(jiǎn)單來(lái)講,jwt就是將原始的json數(shù)據(jù)格式進(jìn)行了安全的封裝,這樣就可以直接基于jwt在通信雙方安全的進(jìn)行信息傳輸了。
JWT的組成: (JWT令牌由三個(gè)部分組成,三個(gè)部分之間使用英文的點(diǎn)來(lái)分割)
- 第一部分:Header(頭), 記錄令牌類型、簽名算法等。 例如:{"alg":"HS256","type":"JWT"}
- 第二部分:Payload(有效載荷),攜帶一些自定義信息、默認(rèn)信息等。 例如:{"id":"1","username":"Tom"}
- 第三部分:Signature(簽名),防止Token被篡改、確保安全性。將header、payload,并加入指定秘鑰,通過(guò)指定簽名算法計(jì)算而來(lái)。
簽名的目的就是為了防jwt令牌被篡改,而正是因?yàn)閖wt令牌最后一個(gè)部分?jǐn)?shù)字簽名的存在,所以整個(gè)jwt 令牌是非常安全可靠的。一旦jwt令牌當(dāng)中任何一個(gè)部分、任何一個(gè)字符被篡改了,整個(gè)令牌在校驗(yàn)的時(shí)候都會(huì)失敗,所以它是非常安全可靠的。

JWT是如何將原始的JSON格式數(shù)據(jù),轉(zhuǎn)變?yōu)樽址哪兀?/p>
其實(shí)在生成JWT令牌時(shí),會(huì)對(duì)JSON格式的數(shù)據(jù)進(jìn)行一次編碼:進(jìn)行base64編碼
Base64:是一種基于64個(gè)可打印的字符來(lái)表示二進(jìn)制數(shù)據(jù)的編碼方式。既然能編碼,那也就意味著也能解碼。所使用的64個(gè)字符分別是A到Z、a到z、 0- 9,一個(gè)加號(hào),一個(gè)斜杠,加起來(lái)就是64個(gè)字符。任何數(shù)據(jù)經(jīng)過(guò)base64編碼之后,最終就會(huì)通過(guò)這64個(gè)字符來(lái)表示。當(dāng)然還有一個(gè)符號(hào),那就是等號(hào)。等號(hào)它是一個(gè)補(bǔ)位的符號(hào)
需要注意的是Base64是編碼方式,而不是加密方式。

JWT令牌最典型的應(yīng)用場(chǎng)景就是登錄認(rèn)證:
在瀏覽器發(fā)起請(qǐng)求來(lái)執(zhí)行登錄操作,此時(shí)會(huì)訪問(wèn)登錄的接口,如果登錄成功之后,我們需要生成一個(gè)jwt令牌,將生成的 jwt令牌返回給前端。
前端拿到j(luò)wt令牌之后,會(huì)將jwt令牌存儲(chǔ)起來(lái)。在后續(xù)的每一次請(qǐng)求中都會(huì)將jwt令牌攜帶到服務(wù)端。
服務(wù)端統(tǒng)一攔截請(qǐng)求之后,先來(lái)判斷一下這次請(qǐng)求有沒(méi)有把令牌帶過(guò)來(lái),如果沒(méi)有帶過(guò)來(lái),直接拒絕訪問(wèn),如果帶過(guò)來(lái)了,還要校驗(yàn)一下令牌是否是有效。如果有效,就直接放行進(jìn)行請(qǐng)求的處理。
在JWT登錄認(rèn)證的場(chǎng)景中我們發(fā)現(xiàn),整個(gè)流程當(dāng)中涉及到兩步操作:
在登錄成功之后,要生成令牌。
每一次請(qǐng)求當(dāng)中,要接收令牌并對(duì)令牌進(jìn)行校驗(yàn)。
生成和校驗(yàn)
首先我們先來(lái)實(shí)現(xiàn)JWT令牌的生成。要想使用JWT令牌,需要先引入JWT的依賴:
<!-- JWT依賴-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>在引入完JWT來(lái)賴后,就可以調(diào)用工具包中提供的API來(lái)完成JWT令牌的生成和校驗(yàn)
工具類:Jwts
生成JWT代碼實(shí)現(xiàn):
@Test
public void genJwt(){
Map<String,Object> claims = new HashMap<>();
claims.put("id",1);
claims.put("username","Tom");
String jwt = Jwts.builder()
.setClaims(claims) //自定義內(nèi)容(載荷)
.signWith(SignatureAlgorithm.HS256, "itheima") //簽名算法
.setExpiration(new Date(System.currentTimeMillis() + 24*3600*1000)) //有效期
.compact();
System.out.println(jwt);
}運(yùn)行測(cè)試方法:
eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwiZXhwIjoxNjcyNzI5NzMwfQ.fHi0Ub8npbyt71UqLXDdLyipptLgxBUg_mSuGJtXtBk
輸出的結(jié)果就是生成的JWT令牌,,通過(guò)英文的點(diǎn)分割對(duì)三個(gè)部分進(jìn)行分割,我們可以將生成的令牌復(fù)制一下,然后打開(kāi)JWT的官網(wǎng),將生成的令牌直接放在Encoded位置,此時(shí)就會(huì)自動(dòng)的將令牌解析出來(lái)。

第一部分解析出來(lái),看到JSON格式的原始數(shù)據(jù),所使用的簽名算法為HS256。
第二個(gè)部分是我們自定義的數(shù)據(jù),之前我們自定義的數(shù)據(jù)就是id,還有一個(gè)exp代表的是我們所設(shè)置的過(guò)期時(shí)間。
由于前兩個(gè)部分是base64編碼,所以是可以直接解碼出來(lái)。但最后一個(gè)部分并不是base64編碼,是經(jīng)過(guò)簽名算法計(jì)算出來(lái)的,所以最后一個(gè)部分是不會(huì)解析的。
實(shí)現(xiàn)了JWT令牌的生成,下面我們接著使用Java代碼來(lái)校驗(yàn)JWT令牌(解析生成的令牌):
@Test
public void parseJwt(){
Claims claims = Jwts.parser()
.setSigningKey("itheima")//指定簽名密鑰(必須保證和生成令牌時(shí)使用相同的簽名密鑰)
.parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwiZXhwIjoxNjcyNzI5NzMwfQ.fHi0Ub8npbyt71UqLXDdLyipptLgxBUg_mSuGJtXtBk")
.getBody();
System.out.println(claims);
}運(yùn)行測(cè)試方法:
{id=1, exp=1672729730}
令牌解析后,我們可以看到id和過(guò)期時(shí)間,如果在解析的過(guò)程當(dāng)中沒(méi)有報(bào)錯(cuò),就說(shuō)明解析成功了。
下面我們做一個(gè)測(cè)試:把令牌header中的數(shù)字9變?yōu)?,運(yùn)行測(cè)試方法后發(fā)現(xiàn)報(bào)錯(cuò):
原h(huán)eader: eyJhbGciOiJIUzI1NiJ9
修改為: eyJhbGciOiJIUzI1NiJ8

結(jié)論:篡改令牌中的任何一個(gè)字符,在對(duì)令牌進(jìn)行解析時(shí)都會(huì)報(bào)錯(cuò),所以JWT令牌是非常安全可靠的。
我們繼續(xù)測(cè)試:修改生成令牌的時(shí)指定的過(guò)期時(shí)間,修改為1分鐘
@Test
public void genJwt(){
Map<String,Object> claims = new HashMap<>();
claims.put(“id”,1);
claims.put(“username”,“Tom”);
String jwt = Jwts.builder()
.setClaims(claims) //自定義內(nèi)容(載荷)
.signWith(SignatureAlgorithm.HS256, “itheima”) //簽名算法
.setExpiration(new Date(System.currentTimeMillis() + 60*1000)) //有效期60秒
.compact();
System.out.println(jwt);
//輸出結(jié)果:eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwiZXhwIjoxNjczMDA5NzU0fQ.RcVIR65AkGiax-ID6FjW60eLFH3tPTKdoK7UtE4A1ro
}
@Test
public void parseJwt(){
Claims claims = Jwts.parser()
.setSigningKey("itheima")//指定簽名密鑰
.parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwiZXhwIjoxNjczMDA5NzU0fQ.RcVIR65AkGiax-ID6FjW60eLFH3tPTKdoK7UtE4A1ro")
.getBody();
System.out.println(claims);
}等待1分鐘之后運(yùn)行測(cè)試方法發(fā)現(xiàn)也報(bào)錯(cuò)了,說(shuō)明:JWT令牌過(guò)期后,令牌就失效了,解析的為非法令牌。
通過(guò)以上測(cè)試,我們?cè)谑褂肑WT令牌時(shí)需要注意:
JWT校驗(yàn)時(shí)使用的簽名秘鑰,必須和生成JWT令牌時(shí)使用的秘鑰是配套的。
如果JWT令牌解析校驗(yàn)時(shí)報(bào)錯(cuò),則說(shuō)明 JWT令牌被篡改 或 失效了,令牌非法。
登錄下發(fā)令牌
JWT令牌的生成和校驗(yàn)的基本操作我們已經(jīng)學(xué)習(xí)完了,接下來(lái)我們就需要在案例當(dāng)中通過(guò)JWT令牌技術(shù)來(lái)跟蹤會(huì)話。具體的思路我們前面已經(jīng)分析過(guò)了,主要就是兩步操作:
生成令牌
在登錄成功之后來(lái)生成一個(gè)JWT令牌,并且把這個(gè)令牌直接返回給前端
校驗(yàn)令牌
攔截前端請(qǐng)求,從請(qǐng)求中獲取到令牌,對(duì)令牌進(jìn)行解析校驗(yàn)
那我們首先來(lái)完成:登錄成功之后生成JWT令牌,并且把令牌返回給前端。
實(shí)現(xiàn)步驟:
引入JWT工具類
在項(xiàng)目工程下創(chuàng)建com.itheima.utils包,并把提供JWT工具類復(fù)制到該包下
登錄完成后,調(diào)用工具類生成JWT令牌并返回
JWT工具類
public class JwtUtils {
private static String signKey = "itheima";//簽名密鑰
private static Long expire = 43200000L; //有效時(shí)間
/**
* 生成JWT令牌
* @param claims JWT第二部分負(fù)載 payload 中存儲(chǔ)的內(nèi)容
* @return
*/
public static String generateJwt(Map<String, Object> claims){
String jwt = Jwts.builder()
.addClaims(claims)//自定義信息(有效載荷)
.signWith(SignatureAlgorithm.HS256, signKey)//簽名算法(頭部)
.setExpiration(new Date(System.currentTimeMillis() + expire))//過(guò)期時(shí)間
.compact();
return jwt;
}
/**
* 解析JWT令牌
* @param jwt JWT令牌
* @return JWT第二部分負(fù)載 payload 中存儲(chǔ)的內(nèi)容
*/
public static Claims parseJWT(String jwt){
Claims claims = Jwts.parser()
.setSigningKey(signKey)//指定簽名密鑰
.parseClaimsJws(jwt)//指定令牌Token
.getBody();
return claims;
}
}
登錄成功,生成JWT令牌并返回 :
@RestController
@Slf4j
public class LoginController {
//依賴業(yè)務(wù)層對(duì)象
@Autowired
private EmpService empService;
@PostMapping("/login")
public Result login(@RequestBody Emp emp) {
//調(diào)用業(yè)務(wù)層:登錄功能
Emp loginEmp = empService.login(emp);
//判斷:登錄用戶是否存在
if(loginEmp !=null ){
//自定義信息
Map<String , Object> claims = new HashMap<>();
claims.put("id", loginEmp.getId());
claims.put("username",loginEmp.getUsername());
claims.put("name",loginEmp.getName());
//使用JWT工具類,生成身份令牌
String token = JwtUtils.generateJwt(claims);
return Result.success(token);
}
return Result.error("用戶名或密碼錯(cuò)誤");
}
}重啟服務(wù),打開(kāi)postman測(cè)試登錄接口:

打開(kāi)瀏覽器完成前后端聯(lián)調(diào)操作:利用開(kāi)發(fā)者工具,抓取一下網(wǎng)絡(luò)請(qǐng)求

登錄請(qǐng)求完成后,可以看到JWT令牌已經(jīng)響應(yīng)給了前端,此時(shí)前端就會(huì)將JWT令牌存儲(chǔ)在瀏覽器本地。
服務(wù)器響應(yīng)的JWT令牌存儲(chǔ)在本地瀏覽器哪里了呢?
在當(dāng)前案例中,JWT令牌存儲(chǔ)在瀏覽器的本地存儲(chǔ)空間local storage中了。 local storage是瀏覽器的本地存儲(chǔ),在移動(dòng)端也是支持的。

我們?cè)诎l(fā)起一個(gè)查詢部門數(shù)據(jù)的請(qǐng)求,此時(shí)我們可以看到在請(qǐng)求頭中包含一個(gè)token(JWT令牌),后續(xù)的每一次請(qǐng)求當(dāng)中,都會(huì)將這個(gè)令牌攜帶到服務(wù)端。

到此這篇關(guān)于SpringBoot JWT令牌的使用的文章就介紹到這了,更多相關(guān)SpringBoot JWT令牌內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java AOP實(shí)現(xiàn)自定義滑動(dòng)窗口限流器方法詳解
這篇文章主要介紹了Java AOP實(shí)現(xiàn)自定義滑動(dòng)窗口限流器方法,其中滑動(dòng)窗口算法彌補(bǔ)了計(jì)數(shù)器算法的不足,滑動(dòng)窗口算法把間隔時(shí)間劃分成更小的粒度,當(dāng)更小粒度的時(shí)間間隔過(guò)去后,把過(guò)去的間隔請(qǐng)求數(shù)減掉,再補(bǔ)充一個(gè)空的時(shí)間間隔,需要的朋友可以參考下2022-07-07
一文徹底吃透SpringMVC中的轉(zhuǎn)發(fā)和重定向
大家應(yīng)該都知道springmvc本來(lái)就會(huì)把返回的字符串作為視圖名解析,然后轉(zhuǎn)發(fā)到對(duì)應(yīng)的視圖,這篇文章主要給大家介紹了關(guān)于SpringMVC中轉(zhuǎn)發(fā)和重定向的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-04-04
springboot項(xiàng)目配置swagger2示例詳解
Swagger是一款RESTful接口的文檔在線自動(dòng)生成、功能測(cè)試功能框架。本文重點(diǎn)給大家介紹springboot項(xiàng)目配置swagger2示例代碼詳解,需要的朋友參考下吧2021-09-09
SpringBoot集成內(nèi)存數(shù)據(jù)庫(kù)hsqldb的實(shí)踐
hsqldb只需要添加對(duì)應(yīng)的依賴,然后在配置文件進(jìn)行配置。不需要安裝一個(gè)數(shù)據(jù)庫(kù),本文就來(lái)介紹一下具體使用,感興趣的可以了解一下2021-09-09
淺析SpringBoot實(shí)現(xiàn)防盜鏈的五大常見(jiàn)漏洞和解決方法
文章詳細(xì)分析了Spring?Boot防盜鏈的五大致命漏洞,包括僅依賴Referer校驗(yàn),未處理CDN代理穿透問(wèn)題,不使用動(dòng)態(tài)Token驗(yàn)證和未實(shí)現(xiàn)流量監(jiān)控與異常處理,每個(gè)漏洞都提供了代碼示例,并給出了解決方案2026-05-05
SpringBoot?JavaMailSender發(fā)送郵件功能(實(shí)例詳解)
JavaMailSender是Spring提供的,非常好用的,實(shí)現(xiàn)郵件發(fā)送的接口 ,這篇文章主要介紹了SpringBoot?JavaMailSender發(fā)送郵件功能,需要的朋友可以參考下2024-03-03
SpringBoot優(yōu)化啟動(dòng)速度實(shí)現(xiàn)過(guò)程
本文主要介紹了SpringBoot啟動(dòng)慢的原因,并提供了多種針對(duì)性優(yōu)化方案,如延遲Bean初始化、關(guān)閉JMX、減少組件掃描、升級(jí)JDK及SpringBoot版本等,旨在提升應(yīng)用啟動(dòng)速度2026-05-05

