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

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

JWT令牌最典型的應(yīng)用場景就是登錄認(rèn)證:
在瀏覽器發(fā)起請求來執(zhí)行登錄操作,此時(shí)會訪問登錄的接口,如果登錄成功之后,我們需要生成一個(gè)jwt令牌,將生成的 jwt令牌返回給前端。
前端拿到j(luò)wt令牌之后,會將jwt令牌存儲起來。在后續(xù)的每一次請求中都會將jwt令牌攜帶到服務(wù)端。
服務(wù)端統(tǒng)一攔截請求之后,先來判斷一下這次請求有沒有把令牌帶過來,如果沒有帶過來,直接拒絕訪問,如果帶過來了,還要校驗(yàn)一下令牌是否是有效。如果有效,就直接放行進(jìn)行請求的處理。
在JWT登錄認(rèn)證的場景中我們發(fā)現(xiàn),整個(gè)流程當(dāng)中涉及到兩步操作:
在登錄成功之后,要生成令牌。
每一次請求當(dāng)中,要接收令牌并對令牌進(jìn)行校驗(yàn)。
生成和校驗(yàn)
首先我們先來實(shí)現(xiàn)JWT令牌的生成。要想使用JWT令牌,需要先引入JWT的依賴:
<!-- JWT依賴-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>在引入完JWT來賴后,就可以調(diào)用工具包中提供的API來完成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)行測試方法:
eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwiZXhwIjoxNjcyNzI5NzMwfQ.fHi0Ub8npbyt71UqLXDdLyipptLgxBUg_mSuGJtXtBk
輸出的結(jié)果就是生成的JWT令牌,,通過英文的點(diǎn)分割對三個(gè)部分進(jìn)行分割,我們可以將生成的令牌復(fù)制一下,然后打開JWT的官網(wǎng),將生成的令牌直接放在Encoded位置,此時(shí)就會自動(dòng)的將令牌解析出來。

第一部分解析出來,看到JSON格式的原始數(shù)據(jù),所使用的簽名算法為HS256。
第二個(gè)部分是我們自定義的數(shù)據(jù),之前我們自定義的數(shù)據(jù)就是id,還有一個(gè)exp代表的是我們所設(shè)置的過期時(shí)間。
由于前兩個(gè)部分是base64編碼,所以是可以直接解碼出來。但最后一個(gè)部分并不是base64編碼,是經(jīng)過簽名算法計(jì)算出來的,所以最后一個(gè)部分是不會解析的。
實(shí)現(xiàn)了JWT令牌的生成,下面我們接著使用Java代碼來校驗(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)行測試方法:
{id=1, exp=1672729730}
令牌解析后,我們可以看到id和過期時(shí)間,如果在解析的過程當(dāng)中沒有報(bào)錯(cuò),就說明解析成功了。
下面我們做一個(gè)測試:把令牌header中的數(shù)字9變?yōu)?,運(yùn)行測試方法后發(fā)現(xiàn)報(bào)錯(cuò):
原h(huán)eader: eyJhbGciOiJIUzI1NiJ9
修改為: eyJhbGciOiJIUzI1NiJ8

結(jié)論:篡改令牌中的任何一個(gè)字符,在對令牌進(jìn)行解析時(shí)都會報(bào)錯(cuò),所以JWT令牌是非常安全可靠的。
我們繼續(xù)測試:修改生成令牌的時(shí)指定的過期時(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)行測試方法發(fā)現(xiàn)也報(bào)錯(cuò)了,說明:JWT令牌過期后,令牌就失效了,解析的為非法令牌。
通過以上測試,我們在使用JWT令牌時(shí)需要注意:
JWT校驗(yàn)時(shí)使用的簽名秘鑰,必須和生成JWT令牌時(shí)使用的秘鑰是配套的。
如果JWT令牌解析校驗(yàn)時(shí)報(bào)錯(cuò),則說明 JWT令牌被篡改 或 失效了,令牌非法。
登錄下發(fā)令牌
JWT令牌的生成和校驗(yàn)的基本操作我們已經(jīng)學(xué)習(xí)完了,接下來我們就需要在案例當(dāng)中通過JWT令牌技術(shù)來跟蹤會話。具體的思路我們前面已經(jīng)分析過了,主要就是兩步操作:
生成令牌
在登錄成功之后來生成一個(gè)JWT令牌,并且把這個(gè)令牌直接返回給前端
校驗(yàn)令牌
攔截前端請求,從請求中獲取到令牌,對令牌進(jìn)行解析校驗(yàn)
那我們首先來完成:登錄成功之后生成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 中存儲的內(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))//過期時(shí)間
.compact();
return jwt;
}
/**
* 解析JWT令牌
* @param jwt JWT令牌
* @return JWT第二部分負(fù)載 payload 中存儲的內(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ù)層對象
@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ù),打開postman測試登錄接口:

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

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

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

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

