JAVA后端實(shí)現(xiàn)JWT令牌的示例
首先解釋一下JWT,在此之前,我們需要明確為什么需要JWT
登陸校驗(yàn)
其實(shí)最常見(jiàn)的應(yīng)用場(chǎng)景就是登陸校驗(yàn),我們希望某個(gè)用戶(hù)在初次打開(kāi)網(wǎng)址時(shí),首先應(yīng)該進(jìn)行登陸操作,而不是直接訪(fǎng)問(wèn)某個(gè)功能,并且在登陸之后,訪(fǎng)問(wèn)其他功能時(shí)不需要再次登陸。在此前提下,應(yīng)運(yùn)而生三種登陸校驗(yàn)方式,下面我們一一講解。
會(huì)話(huà)跟蹤
什么是會(huì)話(huà),我們摒棄其他科普詞條繁雜的專(zhuān)業(yè)詞匯介紹,我們就這樣認(rèn)為:你去買(mǎi)東西,從你走進(jìn)了一家商店開(kāi)始,你就開(kāi)始了你和“商店”的會(huì)話(huà),你買(mǎi)完?yáng)|西走出商店,此次會(huì)話(huà)結(jié)束。在這里面,“你”就是瀏覽器,“商店”就是服務(wù)器。我們買(mǎi)東西,不可能每買(mǎi)一個(gè),都需要重新進(jìn)入商店(登錄),也不可能不進(jìn)入商店,就可以買(mǎi)東西(舉例,以線(xiàn)下商店為例)。
接下來(lái)我們正式講解什么是會(huì)話(huà)跟蹤,詞條解釋是這樣的:一種維護(hù)瀏覽器狀態(tài)的方法,服務(wù)器需要識(shí)別多次請(qǐng)求是否來(lái)自于同一瀏覽器,以便在同一次會(huì)話(huà)的多次請(qǐng)求間共享數(shù)據(jù)。
為什么我們需要會(huì)話(huà)跟蹤,這是因?yàn)镠TTP協(xié)議是一種無(wú)狀態(tài)的協(xié)議。所謂無(wú)狀態(tài),指的是每一次請(qǐng)求都是獨(dú)立的,下一次請(qǐng)求并不會(huì)攜帶上一次請(qǐng)求的數(shù)據(jù)。而瀏覽器與服務(wù)器之間進(jìn)行交互,基于HTTP協(xié)議也就意味著現(xiàn)在我們通過(guò)瀏覽器來(lái)訪(fǎng)問(wèn)了登陸這個(gè)接口,實(shí)現(xiàn)了登陸的操作,接下來(lái)我們?cè)趫?zhí)行其他業(yè)務(wù)操作時(shí),服務(wù)器也并不知道這個(gè)員工到底登陸了沒(méi)有。因?yàn)镠TTP協(xié)議是無(wú)狀態(tài)的,兩次請(qǐng)求之間是獨(dú)立的,所以是無(wú)法判斷這個(gè)員工到底登陸了沒(méi)有。因此我們需要會(huì)話(huà)跟蹤,去“記錄”交互操作。

我們有三種會(huì)話(huà)跟蹤技術(shù),分別是:cookie、session、令牌(只需了解jwt的可以跳過(guò)前兩項(xiàng)介紹)
cookie
cookie 是客戶(hù)端會(huì)話(huà)跟蹤技術(shù),它是存儲(chǔ)在客戶(hù)端瀏覽器的,我們使用 cookie 來(lái)跟蹤會(huì)話(huà),我們就可以在瀏覽器第一次發(fā)起請(qǐng)求來(lái)請(qǐng)求服務(wù)器的時(shí)候,我們?cè)诜?wù)器端來(lái)設(shè)置一個(gè)cookie。

比如第一次請(qǐng)求了登錄接口,登錄接口執(zhí)行完成之后,我們就可以設(shè)置一個(gè)cookie,在 cookie 當(dāng)中我們就可以來(lái)存儲(chǔ)用戶(hù)相關(guān)的一些數(shù)據(jù)信息。比如我可以在 cookie 當(dāng)中來(lái)存儲(chǔ)當(dāng)前登錄用戶(hù)的用戶(hù)名,用戶(hù)的ID。
服務(wù)器端在給客戶(hù)端在響應(yīng)數(shù)據(jù)的時(shí)候,會(huì)自動(dòng)的將 cookie 響應(yīng)給瀏覽器,瀏覽器接收到響應(yīng)回來(lái)的 cookie 之后,會(huì)自動(dòng)的將 cookie 的值存儲(chǔ)在瀏覽器本地。接下來(lái)在后續(xù)的每一次請(qǐng)求當(dāng)中,都會(huì)將瀏覽器本地所存儲(chǔ)的 cookie 自動(dòng)地?cái)y帶到服務(wù)端。
切記,以上三步都是自動(dòng)進(jìn)行。那么為什么會(huì)自動(dòng)進(jìn)行?
因?yàn)?cookie 它是 HTP 協(xié)議當(dāng)中所支持的技術(shù),而各大瀏覽器廠(chǎng)商都支持了這一標(biāo)準(zhǔn)。在 HTTP 協(xié)議官方給我們提供了一個(gè)響應(yīng)頭和請(qǐng)求頭:
響應(yīng)頭 Set-Cookie :設(shè)置Cookie數(shù)據(jù)的
請(qǐng)求頭 Cookie:攜帶Cookie數(shù)據(jù)的

接下來(lái)在服務(wù)端我們就可以獲取到 cookie 的值。我們可以去判斷一下這個(gè) cookie 的值是否存在,如果不存在這個(gè)cookie,就說(shuō)明客戶(hù)端之前是沒(méi)有訪(fǎng)問(wèn)登錄接口的;如果存在 cookie 的值,就說(shuō)明客戶(hù)端之前已經(jīng)登錄完成了。這樣我們就可以基于 cookie 在同一次會(huì)話(huà)的不同請(qǐng)求之間來(lái)共享數(shù)據(jù)。
我們使用代碼實(shí)現(xiàn):
@Slf4j
@RestController
public class SessionController {
//設(shè)置Cookie
@GetMapping("/c1")
public Result cookie1(HttpServletResponse response){
response.addCookie(new Cookie("login_username","itheima")); //設(shè)置Cookie/響應(yīng)Cookie
return Result.success();
}
//獲取Cookie
@GetMapping("/c2")
public Result cookie2(HttpServletRequest request){
Cookie[] cookies = request.getCookies();
for (Cookie cookie : cookies) {
if(cookie.getName().equals("login_username")){
System.out.println("login_username: "+cookie.getValue()); //輸出name為login_username的cookie
}
}
return Result.success();
}
} 訪(fǎng)問(wèn)c1接口,設(shè)置Cookie,http://localhost:8080/c1

我們可以看到,設(shè)置的cookie,通過(guò)響應(yīng)頭Set-Cookie響應(yīng)給瀏覽器,并且瀏覽器會(huì)將Cookie,存儲(chǔ)在瀏覽器端。

訪(fǎng)問(wèn)c2接口 http://localhost:8080/c2,此時(shí)瀏覽器會(huì)自動(dòng)的將Cookie攜帶到服務(wù)端。

優(yōu)點(diǎn):HTTP協(xié)議中支持的技術(shù)(像Set-Cookie 響應(yīng)頭的解析以及 Cookie 請(qǐng)求頭數(shù)據(jù)的攜帶,都是瀏覽器自動(dòng)進(jìn)行的,是無(wú)需我們手動(dòng)操作的)
缺點(diǎn):
移動(dòng)端APP(Android、IOS)中無(wú)法使用Cookie
不安全,用戶(hù)可以自己禁用Cookie
Cookie不能跨域(跨域參考:http://www.dhdzp.com/javascript/333320pbv.htm)
session
其實(shí)session的底層就是基于我們剛才所介紹的 Cookie 來(lái)實(shí)現(xiàn)的。
獲取Session
如果我們現(xiàn)在要基于 Session 來(lái)進(jìn)行會(huì)話(huà)跟蹤,瀏覽器在第一次請(qǐng)求服務(wù)器的時(shí)候,我們就可以直接在服務(wù)器當(dāng)中來(lái)獲取到會(huì)話(huà)對(duì)象Session。如果是第一次請(qǐng)求Session ,會(huì)話(huà)對(duì)象是不存在的,這個(gè)時(shí)候服務(wù)器會(huì)自動(dòng)的創(chuàng)建一個(gè)會(huì)話(huà)對(duì)象Session 。而每一個(gè)會(huì)話(huà)對(duì)象Session ,它都有一個(gè)ID(示意圖中Session后面括號(hào)中的1,就表示ID),我們稱(chēng)之為 Session 的ID。

響應(yīng)Cookie (JSESSIONID)
接下來(lái),服務(wù)器端在給瀏覽器響應(yīng)數(shù)據(jù)的時(shí)候,它會(huì)將 Session 的 ID 通過(guò) Cookie 響應(yīng)給瀏覽器。其實(shí)在響應(yīng)頭當(dāng)中增加了一個(gè) Set-Cookie 響應(yīng)頭。這個(gè) Set-Cookie 響應(yīng)頭對(duì)應(yīng)的值是不是cookie? cookie 的名字是固定的 JSESSIONID 代表的服務(wù)器端會(huì)話(huà)對(duì)象 Session 的 ID。瀏覽器會(huì)自動(dòng)識(shí)別這個(gè)響應(yīng)頭,然后自動(dòng)將Cookie存儲(chǔ)在瀏覽器本地。

查找Session
接下來(lái),在后續(xù)的每一次請(qǐng)求當(dāng)中,都會(huì)將 Cookie 的數(shù)據(jù)獲取出來(lái),并且攜帶到服務(wù)端。接下來(lái)服務(wù)器拿到JSESSIONID這個(gè) Cookie 的值,也就是 Session 的ID。拿到 ID 之后,就會(huì)從眾多的 Session 當(dāng)中來(lái)找到當(dāng)前請(qǐng)求對(duì)應(yīng)的會(huì)話(huà)對(duì)象Session。

這樣我們是不是就可以通過(guò) Session 會(huì)話(huà)對(duì)象在同一次會(huì)話(huà)的多次請(qǐng)求之間來(lái)共享數(shù)據(jù)了?好,這就是基于 Session 進(jìn)行會(huì)話(huà)跟蹤的流程。
我們使用代碼實(shí)現(xiàn):
@Slf4j
@RestController
public class SessionController {
@GetMapping("/s1")
public Result session1(HttpSession session){
log.info("HttpSession-s1: {}", session.hashCode());
session.setAttribute("loginUser", "tom"); //往session中存儲(chǔ)數(shù)據(jù)
return Result.success();
}
@GetMapping("/s2")
public Result session2(HttpServletRequest request){
HttpSession session = request.getSession();
log.info("HttpSession-s2: {}", session.hashCode());
Object loginUser = session.getAttribute("loginUser"); //從session中獲取數(shù)據(jù)
log.info("loginUser: {}", loginUser);
return Result.success(loginUser);
}
}訪(fǎng)問(wèn) s1 接口,http://localhost:8080/s1

請(qǐng)求完成之后,在響應(yīng)頭中,就會(huì)看到有一個(gè)Set-Cookie的響應(yīng)頭,里面響應(yīng)回來(lái)了一個(gè)Cookie,就是JSESSIONID,這個(gè)就是服務(wù)端會(huì)話(huà)對(duì)象 Session 的ID。
訪(fǎng)問(wèn) s2 接口,http://localhost:8080/s2

接下來(lái),在后續(xù)的每次請(qǐng)求時(shí),都會(huì)將Cookie的值,攜帶到服務(wù)端,那服務(wù)端呢,接收到Cookie之后,會(huì)自動(dòng)的根據(jù)JSESSIONID的值,找到對(duì)應(yīng)的會(huì)話(huà)對(duì)象Session。 兩次請(qǐng)求,獲取到的Session會(huì)話(huà)對(duì)象的hashcode是一樣的,就說(shuō)明是同一個(gè)會(huì)話(huà)對(duì)象。而且,第一次請(qǐng)求時(shí),往Session會(huì)話(huà)對(duì)象中存儲(chǔ)的值,第二次請(qǐng)求時(shí),也獲取到了。 那這樣,我們就可以通過(guò)Session會(huì)話(huà)對(duì)象,在同一個(gè)會(huì)話(huà)的多次請(qǐng)求之間來(lái)進(jìn)行數(shù)據(jù)共享了。
優(yōu)缺點(diǎn)
優(yōu)點(diǎn):Session是存儲(chǔ)在服務(wù)端的,安全
缺點(diǎn):
服務(wù)器集群環(huán)境下無(wú)法直接使用Session
移動(dòng)端APP(Android、IOS)中無(wú)法使用Cookie
用戶(hù)可以自己禁用Cookie
Cookie不能跨域
是的,想必你們也發(fā)現(xiàn)了,cookie的缺點(diǎn),session也有,畢竟session就是基于cookie實(shí)現(xiàn)的。
下面我們開(kāi)始本文目標(biāo):JWT
令牌
這里我們所提到的令牌,其實(shí)它就是一個(gè)用戶(hù)身份的標(biāo)識(shí),看似很高大上,很神秘,其實(shí)本質(zhì)就是一個(gè)字符串。想必很多90后都曾經(jīng)擁有過(guò)一個(gè)實(shí)體的QQ令牌,并且現(xiàn)在Steam也有登錄的令牌。實(shí)質(zhì)上他們的功能作用是一樣的。其實(shí)它就是一個(gè)用戶(hù)身份的標(biāo)識(shí),本質(zhì)就是一個(gè)字符串。
如果通過(guò)令牌技術(shù)來(lái)跟蹤會(huì)話(huà),我們就可以在瀏覽器發(fā)起請(qǐng)求。在請(qǐng)求登錄接口的時(shí)候,如果登錄成功,我就可以生成一個(gè)令牌,令牌就是用戶(hù)的合法身份憑證。接下來(lái)我在響應(yīng)數(shù)據(jù)的時(shí)候,我就可以直接將令牌響應(yīng)給前端。
接下來(lái)我們?cè)谇岸顺绦虍?dāng)中接收到令牌之后,就需要將這個(gè)令牌存儲(chǔ)起來(lái)。這個(gè)存儲(chǔ)可以存儲(chǔ)在 cookie 當(dāng)中,也可以存儲(chǔ)在其他的存儲(chǔ)空間(比如:localStorage)當(dāng)中。
接下來(lái),在后續(xù)的每一次請(qǐng)求當(dāng)中,都需要將令牌攜帶到服務(wù)端。攜帶到服務(wù)端之后,接下來(lái)我們就需要來(lái)校驗(yàn)令牌的有效性。如果令牌是有效的,就說(shuō)明用戶(hù)已經(jīng)執(zhí)行了登錄操作,如果令牌是無(wú)效的,就說(shuō)明用戶(hù)之前并未執(zhí)行登錄操作。
此時(shí),如果是在同一次會(huì)話(huà)的多次請(qǐng)求之間,我們想共享數(shù)據(jù),我們就可以將共享的數(shù)據(jù)存儲(chǔ)在令牌當(dāng)中就可以了。

優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
支持PC端、移動(dòng)端
解決集群環(huán)境下的認(rèn)證問(wèn)題
減輕服務(wù)器的存儲(chǔ)壓力(無(wú)需在服務(wù)器端存儲(chǔ))
缺點(diǎn):需要自己實(shí)現(xiàn)(包括令牌的生成、令牌的傳遞、令牌的校驗(yàn))
針對(duì)于這三種方案,現(xiàn)在企業(yè)開(kāi)發(fā)當(dāng)中使用的最多的就是第三種令牌技術(shù)進(jìn)行會(huì)話(huà)跟蹤。而前面的這兩種傳統(tǒng)的方案,現(xiàn)在企業(yè)項(xiàng)目開(kāi)發(fā)當(dāng)中已經(jīng)很少使用了。所以在我們的課程當(dāng)中,我們也將會(huì)采用令牌技術(shù)來(lái)解決案例項(xiàng)目當(dāng)中的會(huì)話(huà)跟蹤問(wèn)題。
JWT
JWT全稱(chēng):JSON Web Token (官網(wǎng):JSON Web Tokens - 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ǔ)用戶(hù)的相關(guān)信息。
簡(jiǎn)單來(lái)講,jwt就是將原始的json數(shù)據(jù)格式進(jìn)行了安全的封裝,這樣就可以直接基于jwt在通信雙方安全的進(jìn)行信息傳輸了。
JWT的組成: (JWT令牌由三個(gè)部分組成,三個(gè)部分之間使用英文的點(diǎn)來(lái)分割)
第一部分:Header(頭), 記錄令牌類(lèi)型、簽名算法等。 例如:{"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)證:
1. 在瀏覽器發(fā)起請(qǐng)求來(lái)執(zhí)行登錄操作,此時(shí)會(huì)訪(fǎng)問(wèn)登錄的接口,如果登錄成功之后,我們需要生成一個(gè)jwt令牌,將生成的 jwt令牌返回給前端。
2. 前端拿到j(luò)wt令牌之后,會(huì)將jwt令牌存儲(chǔ)起來(lái)。在后續(xù)的每一次請(qǐng)求中都會(huì)將jwt令牌攜帶到服務(wù)端。
3. 服務(wù)端統(tǒng)一攔截請(qǐng)求之后,先來(lái)判斷一下這次請(qǐng)求有沒(méi)有把令牌帶過(guò)來(lái),如果沒(méi)有帶過(guò)來(lái),直接拒絕訪(fǎng)問(wèn),如果帶過(guò)來(lái)了,還要校驗(yàn)一下令牌是否是有效。如果有效,就直接放行進(jìn)行請(qǐng)求的處理。
在JWT登錄認(rèn)證的場(chǎng)景中我們發(fā)現(xiàn),整個(gè)流程當(dāng)中涉及到兩步操作:
1. 在登錄成功之后,要生成令牌。
2. 每一次請(qǐng)求當(dāng)中,要接收令牌并對(duì)令牌進(jìn)行校驗(yàn)。
簡(jiǎn)單介紹了JWT令牌以及JWT令牌的組成之后,接下來(lái)我們就來(lái)學(xué)習(xí)基于Java代碼如何生成和校驗(yàn)JWT令牌。
首先我們先來(lái)實(shí)現(xiàn)JWT令牌的生成。要想使用JWT令牌,需要先引入JWT的依賴(lài):
<!-- JWT依賴(lài)-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>接下來(lái)我們生成算法:
@Test
public void genJwt(){
Map<String,Object> claims = new HashMap<>();
claims.put("id",1);
claims.put("username","HongKongDoll");
String jwt = Jwts.builder()
.setClaims(claims) //自定義內(nèi)容(載荷)
.signWith(SignatureAlgorithm.HS256, "bug,start") //簽名算法
.setExpiration(new Date(System.currentTimeMillis() + 24*3600*1000)) //有效期
.compact();
System.out.println(jwt);
}走你:

這時(shí)我們使用utools上的插件進(jìn)行解碼(或者找任一在線(xiàn)解碼網(wǎng)站都可)

第一部分解析出來(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("bug,start")//指定簽名密鑰(必須保證和生成令牌時(shí)使用相同的簽名密鑰)
.parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwiZXhwIjoxNzA0ODU1NTk5LCJ1c2VybmFtZSI6IkhvbmdLb25nRG9sbCJ9.JLg62R07Zr_IEZtaZ4oAQNkGoNIdGKrLbcy-OUCTTPU")
.getBody();
System.out.println(claims);
}走我:

令牌解析后,我們可以看到id和過(guò)期時(shí)間,如果在解析的過(guò)程當(dāng)中沒(méi)有報(bào)錯(cuò),就說(shuō)明解析成功了。
接下來(lái),為了驗(yàn)證JWT的可靠性,我們修改JWT中其中任一字母,重新進(jìn)行解析:
走他:

我只是把第一位的e換成了E,結(jié)果就報(bào)錯(cuò):JWT解析異常。說(shuō)明解析JWT只要修改其中任一字符,就會(huì)解析失敗。
這時(shí)候又有一位未來(lái)首付要問(wèn)了,那么還有其他的解析失敗因素嗎?當(dāng)然有啦,那就是過(guò)期時(shí)間,我們?cè)谏厦嬖O(shè)置了過(guò)期時(shí)間,那么我們現(xiàn)在把生成策略中過(guò)期時(shí)間換成1秒過(guò)期:

我們重新解析一下:

這次報(bào)的是JWT過(guò)期異常。
通過(guò)以上測(cè)試,我們?cè)谑褂肑WT令牌時(shí)需要注意:
JWT校驗(yàn)時(shí)使用的簽名秘鑰,必須和生成JWT令牌時(shí)使用的秘鑰是配套的。
如果JWT令牌解析校驗(yàn)時(shí)報(bào)錯(cuò),則說(shuō)明 JWT令牌被篡改 或 過(guò)期失效了,令牌非法。
接下來(lái),我們進(jìn)入實(shí)戰(zhàn)部分:
JWT令牌的生成和校驗(yàn)的基本操作我們已經(jīng)學(xué)習(xí)完了,接下來(lái)我們就需要在案例當(dāng)中通過(guò)JWT令牌技術(shù)來(lái)跟蹤會(huì)話(huà)。具體的思路我們前面已經(jīng)分析過(guò)了,主要就是兩步操作:
生成令牌
在登錄成功之后來(lái)生成一個(gè)JWT令牌,并且把這個(gè)令牌直接返回給前端校驗(yàn)令牌
攔截前端請(qǐng)求,從請(qǐng)求中獲取到令牌,對(duì)令牌進(jìn)行解析校驗(yàn)
那我們首先來(lái)完成:登錄成功之后生成JWT令牌,并且把令牌返回給前端。

在controller同級(jí)下,建一個(gè)包,用來(lái)放我們的工具類(lèi),然后新建一個(gè)class:
/**
* @Description JWT工具類(lèi)
* @Author QingNing
* @Date 2024/1/9
*/
package com.ycg.vue.utils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
import java.util.Map;
public class JwtUtils {
private static final String signKey = "bug,start";//簽名密鑰
private static final Long expire = 3600000L; //有效時(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生成策略:
以下是本人的登陸代碼,僅做參考
@ApiOperation(value = "用戶(hù)登錄")
@PostMapping("/login")
public result user(@RequestBody UserVo userVo){
User user = userService.login(userVo);
//非空判斷
if(!Objects.isNull(user)){
//生成
Map<String , Object> claims = new HashMap<>();
claims.put("id", user.getId());
claims.put("username",user.getUsername());
//使用JWT工具類(lèi),生成身份令牌
String token = JwtUtils.generateJwt(claims);
return result.success(token);
}
return result.error("用戶(hù)名或密碼錯(cuò)誤,請(qǐng)重新輸入");
}我們打開(kāi)swagger進(jìn)行查看是否成功。

我們發(fā)現(xiàn),已經(jīng)返回了token,后續(xù)的每一次請(qǐng)求當(dāng)中,都會(huì)將這個(gè)令牌攜帶到服務(wù)端。
此時(shí)我們只需要在前端的返回方法中,添加回調(diào)方法:

即可將jwt令牌存到請(qǐng)求頭中,在進(jìn)行其他操作時(shí)就會(huì)攜帶jwt令牌,直至本次會(huì)話(huà)結(jié)束。后續(xù)可以使用攔截器進(jìn)行判斷。
至此,我們就完成了JWT令牌的實(shí)現(xiàn)。
到此這篇關(guān)于JAVA后端實(shí)現(xiàn)JWT令牌的示例的文章就介紹到這了,更多相關(guān)JAVA JWT令牌內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SWT(JFace)體驗(yàn)之Sash(活動(dòng)控件)
SWT(JFace)體驗(yàn)之Sash(活動(dòng)控件)2009-06-06
JAVA 截取字符串的三種方法 subString,StringUtils,split實(shí)例詳解
這篇文章給大家介紹JAVA 截取字符串的三種方法 subString,StringUtils,split,每種方法結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2024-12-12
Hadoop源碼分析一架構(gòu)關(guān)系簡(jiǎn)介
本篇是Hadoop源碼分析系列文章第一篇,主要介紹一下Hadoop的基礎(chǔ)簡(jiǎn)介以及框架關(guān)系,后續(xù)本系列文章會(huì)持續(xù)更新,有需要的朋友可以借鑒參考下2021-09-09
Java如何實(shí)現(xiàn)數(shù)據(jù)壓縮所有方式性能測(cè)試
本文介紹了多種壓縮算法及其在Java中的實(shí)現(xiàn),包括LZ4、BZip2、Deflate、Gzip和7z等,LZ4以其高效的壓縮和解壓縮速度而受到青睞,特別是在大數(shù)據(jù)處理場(chǎng)景中,通過(guò)對(duì)比不同壓縮算法的性能和壓縮率,我們選擇了最適合當(dāng)前項(xiàng)目需求的壓縮工具2025-02-02
REST架構(gòu)及RESTful應(yīng)用程序簡(jiǎn)介
這篇文章主要為大家介紹了REST架構(gòu)及RESTful的應(yīng)用程序簡(jiǎn)介,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-03-03
完美解決Java獲取文件路徑出現(xiàn)亂碼的問(wèn)題
今天小編就為大家分享一篇完美解決Java獲取文件路徑出現(xiàn)亂碼的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-07-07
升級(jí)springboot3之自動(dòng)配置導(dǎo)入失效問(wèn)題及解決
這篇文章主要介紹了升級(jí)springboot3之自動(dòng)配置導(dǎo)入失效問(wèn)題及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07

