鄭州外貿(mào)建站做推廣
接著上篇博客的學(xué)習(xí)。上篇博客是在基本完成用戶模塊的注冊(cè)接口的開(kāi)發(fā)以及注冊(cè)時(shí)的參數(shù)合法性校驗(yàn)的基礎(chǔ)上,基本完成用戶模塊的登錄接口的主邏輯以及提到了問(wèn)題:"用戶未登錄,需要通過(guò)登錄,獲取到令牌進(jìn)行登錄認(rèn)證,才能使用其他功能的接口"。具體往回看了解的鏈接如下。springboot實(shí)戰(zhàn)學(xué)習(xí)(6)(用戶模塊的登錄認(rèn)證)(初識(shí)令牌)(JWT)-CSDN博客文章瀏覽閱讀2次。本篇博客是在處理用戶模塊中登錄認(rèn)證時(shí)遇到需要解決的問(wèn)題。因?yàn)槲吹卿洉r(shí),需要做到無(wú)法訪問(wèn)和使用其他功能的接口。也就提到了令牌的作用以及滿足令牌的規(guī)范"JWT"。具體的學(xué)習(xí)下篇博客進(jìn)行學(xué)習(xí)...
https://blog.csdn.net/m0_74363339/article/details/142365524?spm=1001.2014.3001.5502
接下來(lái)就去認(rèn)真了解和學(xué)習(xí)Web登錄認(rèn)證中常用的令牌規(guī)范——>"JWT"。
目錄
一、JWT(令牌規(guī)范)
(1)基本介紹
(2)基本組成
(I)解釋上方圖片(JWT令牌字符串)
(II)總結(jié)組成
二、程序中使用JWT令牌?
(1)回顧與思考
三、JWT令牌的生成
(1)JWT令牌示范
(1)如何生成
(2)如何使用生成"JWT令牌"工具
(I)第一步。導(dǎo)入工具的坐標(biāo)(依賴)
(II)第二步。調(diào)用API,生成令牌。
(III)生成"JWT令牌"的代碼不需要去記憶
(3)IDEA中寫(xiě)單元測(cè)試的方法測(cè)試生成JWT令牌
四、JWT令牌的驗(yàn)證
(1)DEA中寫(xiě)單元測(cè)試的方法測(cè)試"JWT令牌"的驗(yàn)證
(2)測(cè)試因篡改"JWT令牌"導(dǎo)致驗(yàn)證失敗的幾種情況
(I)篡改JWP令牌"頭部部分"進(jìn)行驗(yàn)證。
?編輯
(II)篡改JWP令牌中間"載荷部分"進(jìn)行驗(yàn)證。
(III)篡改JWP令牌尾部"數(shù)字簽名"(篡改密鑰)進(jìn)行驗(yàn)證。
(3)處理因篡改JWT令牌的數(shù)據(jù)導(dǎo)致的異常
(I)如果篡改了頭部和載荷部分的數(shù)據(jù),那么驗(yàn)證失敗。
(II)如果篡改了密鑰數(shù)據(jù),那么驗(yàn)證失敗。
(III)如果超過(guò)設(shè)定的Token令牌過(guò)期時(shí)間,那么驗(yàn)證失敗。
(4)關(guān)于"JWT令牌"驗(yàn)證的注意事項(xiàng)
五、總結(jié)
(1)JWT令牌的組成
(2)JWT令牌的使用
(3)尾言
一、JWT(令牌規(guī)范)
(1)基本介紹
JSON Web Tokens - jwt.ioJSON Web Token (JWT) is a compact URL-safe means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JSON object that is digitally signed using JSON Web Signature (JWS).
- JWT的全稱:JSON Web Token
https://jwt.io/
(也就是用于Web領(lǐng)域的基于JSON格式的令牌)
- 定義了一種簡(jiǎn)潔的、自包含的格式,用于通信雙方以json數(shù)據(jù)格式安全的傳輸信息。
(JSON是一種文本數(shù)據(jù)格式,來(lái)源于JavaScript的對(duì)象語(yǔ)法。)
(2)基本組成
(I)解釋上方圖片(JWT令牌字符串)
- 上面展示的它是前后端交互時(shí)傳輸?shù)淖址?。?span style="background-color:#ffd900;">這個(gè)字符串是由JSON格式的字符串編碼得來(lái)的字符串??梢钥吹缴厦孀址袃蓚€(gè)小".",它就是把這整個(gè)字符串分為三個(gè)部分。而"分割"的每一個(gè)小串,對(duì)應(yīng)著Token令牌的一部分。
- 第一部分是頭。它是由JSON字符串編碼得來(lái)的。這個(gè)部分的字符串會(huì)記錄兩個(gè)信息。第一個(gè)是"alg"(它是加密算法,防篡改),而"type"(是JWT)。
- 第二部分是"有效載荷"。它是也是一段字符串。而在這段字符串中,他會(huì)存放我們的業(yè)務(wù)數(shù)據(jù)。比如存放用戶的id、用戶的username等等。這段字符串編碼后,也會(huì)得到上面Token令牌里面的這一段。?
- 而對(duì)應(yīng)的JSON格式的字符串,是如何轉(zhuǎn)換為上面Token令牌中展示的這一段"特殊長(zhǎng)的字符串"呢?在JWT中會(huì)借助于"base64"這種編碼方式完成。而"base64"可以將任意數(shù)據(jù)轉(zhuǎn)換成64個(gè)可打印字符('a'-'z'、'A'-'Z'、'0'-'9'等等)。這些64個(gè)可打印字符最重要的特點(diǎn)就是:"通用",在任意場(chǎng)景都可以被支持。
- 將JSON格式的字符串轉(zhuǎn)換為64個(gè)可打印的字符串,原因是為了提供Token令牌的實(shí)用性。記得"base64"僅僅是一種編碼方式,而不是加密。因此記得在Token令牌的第二部分"有效載荷",一定注意不要放私密數(shù)據(jù)(比如用戶密碼等),不然不安全。
- 最后一部分叫做"數(shù)字簽名"。它是將第一部分以及第二部分,借助于密鑰和加密算法,通過(guò)加密得來(lái)的。而這里的加密算法,是通過(guò)頭部的"alg"來(lái)制定的。密鑰可以在程序中單獨(dú)配置。
- 有了這個(gè)數(shù)字簽名,就可以防篡改,確保Token是安全的。因?yàn)閷?lái)即使篡改了第一和第二部分,但第三部分是不能篡改的,因?yàn)槭羌用芎蟮淖址?。將?lái)JWT再去解析Token令牌時(shí),通過(guò)解密第三部分,得到"頭部"與"載荷",再拿到解密的內(nèi)容與用戶傳遞的內(nèi)容進(jìn)行比對(duì),如果不一樣,就不讓訪問(wèn)。
(II)總結(jié)組成
二、程序中使用JWT令牌?
(1)回顧與思考
- 回到之前的所完成用戶模塊的注冊(cè)接口與登錄接口的主邏輯。
springboot實(shí)戰(zhàn)學(xué)習(xí)筆記(5)(用戶登錄接口的主邏輯)-CSDN博客
- 現(xiàn)在需要在用戶登錄之后生成一個(gè)"JWT令牌",生成了之后還需要把"JWT令牌"響應(yīng)給瀏覽器。
- 將來(lái)瀏覽器再去訪問(wèn)服務(wù)器上的其它資料時(shí),會(huì)攜帶這個(gè)令牌訪問(wèn)。這時(shí)服務(wù)器就能夠得到這個(gè)令牌,并且還需要去驗(yàn)證這個(gè)令牌的合法性。如果令牌合法、沒(méi)有被篡改,就正常提供服務(wù),反之。
- 需要學(xué)習(xí)如何生成令牌?如何驗(yàn)證令牌?
三、JWT令牌的生成
(1)JWT令牌示范
(1)如何生成
- 可以自己手寫(xiě),畢竟JWT是一個(gè)令牌的規(guī)范寫(xiě)法。而是規(guī)范,大家都可以實(shí)現(xiàn)的。
- 有人提供了生成"JWT令牌"的工具,所以可以直接使用這些工具即可。
(2)如何使用生成"JWT令牌"工具
(I)第一步。導(dǎo)入工具的坐標(biāo)(依賴)
引入的是java-jwt
引入坐標(biāo)時(shí),報(bào)錯(cuò),記得嘗試刷新一個(gè)Maven
因?yàn)槲覀儺?dāng)前寫(xiě)的"生成令牌"或者"驗(yàn)證令牌"代碼,它僅僅是一個(gè)測(cè)試的代碼。所以需要把它寫(xiě)到單元測(cè)試?yán)锩?。所以還要引入單元測(cè)試的坐標(biāo)
springboot為了更方便的測(cè)試spring程序,提供了springboot整合單元測(cè)試的一個(gè)起步依賴。
添加完畢,一定記得刷新Maven。
(II)第二步。調(diào)用API,生成令牌。
(III)生成"JWT令牌"的代碼不需要去記憶
- 因?yàn)閷?lái)在公司中使用的時(shí)候,都是使用提供好的工具類,然后直接調(diào)用即可
(3)IDEA中寫(xiě)單元測(cè)試的方法測(cè)試生成JWT令牌
- 在test目錄的feisi目錄下,新建一個(gè)類"JwtTest"。在這個(gè)類的內(nèi)部去寫(xiě)單元測(cè)試的代碼。
- 新建一個(gè)方法testGen(),并且需要在方法上添加一個(gè)注解@Test。
- 如果要生成令牌,就要調(diào)用JWT的API。然后先調(diào)用其create()方法。
- 然后利用鏈?zhǔn)骄幊痰姆绞絹?lái)調(diào)用方法。首先調(diào)用第一個(gè)方法"withClaim()",這個(gè)方法的作用是添加"載荷"(前面講過(guò))。
- 這個(gè)添加載荷的方法就是:第一值傳入鍵的名字(name)(如這里設(shè)置為"user",那么將來(lái)這個(gè)載荷就是承載著用戶相關(guān)的信息),第二個(gè)參數(shù)值放一個(gè)map集合,比如user將來(lái)肯定有"id"、"username"等等。
JWT.create().withClaim("user",claims);
- 所以接著就要?jiǎng)?chuàng)建一個(gè)Map類型的集合。指定其鍵是"String"類型,而值是"Object"類型的。
Map<String, Object> claims = new HashMap<>();
- 有了這個(gè)claims集合,就可以調(diào)用方法put()。如下添加。這樣就提供添加"載荷"的方式,把當(dāng)前用戶信息添加進(jìn)去了。
claims.put("id",1);claims.put("username","張三");
- 接下來(lái)JWT.create().后面還可以添加幾個(gè)方法。
- 如.withExpiresAt()。這是添加過(guò)期時(shí)間。也就是登錄時(shí)獲得的令牌是有登錄有效期的,時(shí)間過(guò)了,就需要重新登錄。將來(lái)的Token令牌是有有效期的。
- 里面是需要一個(gè)Data對(duì)象。直接new Data()。但是這個(gè)是當(dāng)前的時(shí)間,所以還需要通過(guò)System提供的獲取當(dāng)前毫秒值的方法,再重新new一個(gè)Data對(duì)象。往后延遲指定一段時(shí)間。
- 當(dāng)前毫秒值+1000*60*60*12(也就是延后12個(gè)小時(shí))。
JWT.create().withClaim("user",claims).withExpiresAt(new Date(System.currentTimeMillis()+1000*60*60*12));
- 如sign()方法。"sign"是簽名的意思。也就是如上面說(shuō)的數(shù)字簽名,加密用的。方法參數(shù)需要指定一個(gè)加密算法。這里選擇HMAC256()這個(gè)算法。
- 在指定算法的時(shí)候,需要指定一個(gè)密鑰。這個(gè)是由自己定就行,就是一個(gè)字符串。但是它是加密的密鑰,所以不要泄露,不然不能防篡改,保證安全。
.sign(Algorithm.HMAC256("feisi"));//指定算法,配置密鑰
- 這幾個(gè)方法一旦調(diào)用,我們的Token令牌就能生成。所以就要去接收一下Token。
package com.feisi;import com.auth0.jwt.JWT; import com.auth0.jwt.algorithms.Algorithm; import org.junit.jupiter.api.Test;import java.util.Date; import java.util.HashMap; import java.util.Map;public class JwtTest {//生成JWT令牌的方法@Testpublic void testGen(){Map<String, Object> claims = new HashMap<>();claims.put("id",1);claims.put("username","張三");//生成JWT代碼String token = JWT.create().withClaim("user",claims) //添加載荷.withExpiresAt(new Date(System.currentTimeMillis()+1000*60*60*12)) //添加過(guò)期時(shí)間.sign(Algorithm.HMAC256("feisi"));//指定算法,配置密鑰//打印輸出到控制臺(tái),查看生成的token令牌System.out.println(token);} }
- 最后運(yùn)行一下這個(gè)測(cè)試類的方法。這樣"JWT令牌"的生成就完成了。
四、JWT令牌的驗(yàn)證
(1)DEA中寫(xiě)單元測(cè)試的方法測(cè)試"JWT令牌"的驗(yàn)證
- 首先這一部分的代碼也不需要去背或者記憶。
- 接著上面,在"testGen()"方法下添加一個(gè)方法"testParse()"。
- 也是一樣記得添加單元測(cè)試的注解@Test。
- 接著定義一個(gè)字符串"token",模擬存儲(chǔ)用戶傳遞給瀏覽器的token令牌。
- 而這個(gè)token的值就用上面控制臺(tái)打印的token令牌字符串就行。
//驗(yàn)證JWT令牌的方法@Testpublic void testParse(){//定義字符串,模擬用戶傳遞給瀏覽器的token令牌//這個(gè)token就用上面生成的token令牌字符串就行String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"+".eyJ1c2VyIjp7ImlkIjoxLCJ1c2VybmFtZSI6IuW8oOS4iSJ9LCJleHAiOjE3MjcwMzg2MDl9"+".LgfVNY-OAkjAps8yQXDkKyCIRbWw5jNQiNZwwbMf2F4";}
- 接著開(kāi)始提供驗(yàn)證了。去提供調(diào)用JWT這個(gè)類里面的一個(gè)靜態(tài)方法"require()"。這是申請(qǐng)一個(gè)JWT的驗(yàn)證器。
- 而這個(gè)方法里面的參數(shù)需要傳遞一個(gè)算法。之前加密用的算法,解密也要同樣用一樣的算法。直接復(fù)制過(guò)來(lái)。
- 然后再調(diào)用一個(gè)".build()"方法去生成驗(yàn)證器。再用一個(gè)變量"jwtVerifier"給他存起來(lái)。
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("feisi")).build();
- 有了這個(gè)變量之后,就可以調(diào)用驗(yàn)證器的方法,去驗(yàn)證這個(gè)token令牌字符串。
- 調(diào)用方法".verify()",把之前控制臺(tái)的token傳遞給它。
- 它可以去解析這個(gè)token令牌。然后生成一個(gè)解析后的JWT對(duì)象。
DecodedJWT decodedJWT = jwtVerifier.verify(token); //驗(yàn)證token,生成一個(gè)解析后的JWT對(duì)象
- 如果能正常的解析成功之后。那么就意味著可以從變量"decodedJWT"里面獲取到當(dāng)前的"頭部"或者"載荷"以及"數(shù)字簽名"等等。
- 則調(diào)用它的一個(gè)方法:"GetClaims()",而它會(huì)得到所有的"載荷"。用Map對(duì)象的變量去接收一下它。
Map<String, Claim> claims = decodedJWT.getClaims();
- 然后我們指定獲取鍵名(name)為"user"的載荷。并且把得到的結(jié)果輸出到控制臺(tái)里面。
package com.feisi;import com.auth0.jwt.JWT; import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.interfaces.Claim; import com.auth0.jwt.interfaces.DecodedJWT; import org.junit.jupiter.api.Test;import java.util.Date; import java.util.HashMap; import java.util.Map;public class JwtTest {//生成JWT令牌的方法@Testpublic void testGen(){Map<String, Object> claims = new HashMap<>();claims.put("id",1);claims.put("username","張三");//生成JWT代碼String token = JWT.create().withClaim("user",claims) //添加載荷.withExpiresAt(new Date(System.currentTimeMillis()+1000*60*60*12)) //添加過(guò)期時(shí)間.sign(Algorithm.HMAC256("feisi"));//指定算法,配置密鑰//打印輸出到控制臺(tái),查看生成的token令牌System.out.println(token);}//驗(yàn)證JWT令牌的方法@Testpublic void testParse(){//定義字符串,模擬用戶傳遞給瀏覽器的token令牌//這個(gè)token就用上面生成的token令牌字符串就行String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"+".eyJ1c2VyIjp7ImlkIjoxLCJ1c2VybmFtZSI6IuW8oOS4iSJ9LCJleHAiOjE3MjcwMzg2MDl9"+".LgfVNY-OAkjAps8yQXDkKyCIRbWw5jNQiNZwwbMf2F4";JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("feisi")).build();DecodedJWT decodedJWT = jwtVerifier.verify(token); //驗(yàn)證token,生成一個(gè)解析后的JWT對(duì)象Map<String, Claim> claims = decodedJWT.getClaims();System.out.println(claims.get("user"));} }
- 得到控制臺(tái)的正常輸出,如:"id,1"、"username,"張三""等等就解析成功了!
- 正常的解析成功如下。
(2)測(cè)試因篡改"JWT令牌"導(dǎo)致驗(yàn)證失敗的幾種情況
(I)篡改JWP令牌"頭部部分"進(jìn)行驗(yàn)證。
(II)篡改JWP令牌中間"載荷部分"進(jìn)行驗(yàn)證。
所以我們的JWT令牌是具體放篡改的功能的。
(III)篡改JWP令牌尾部"數(shù)字簽名"(篡改密鑰)進(jìn)行驗(yàn)證。
- 篡改了原來(lái)設(shè)定的密鑰"feisi"改成"fesi"。
(3)處理因篡改JWT令牌的數(shù)據(jù)導(dǎo)致的異常
(I)如果篡改了頭部和載荷部分的數(shù)據(jù),那么驗(yàn)證失敗。
(II)如果篡改了密鑰數(shù)據(jù),那么驗(yàn)證失敗。
(III)如果超過(guò)設(shè)定的Token令牌過(guò)期時(shí)間,那么驗(yàn)證失敗。
(Token過(guò)期)
- 例如如下情況:報(bào)錯(cuò)提示"這個(gè)Token令牌"已經(jīng)過(guò)期。
(4)關(guān)于"JWT令牌"驗(yàn)證的注意事項(xiàng)
五、總結(jié)
(1)JWT令牌的組成
- "載荷"這一塊要注意。不要存放一些私密信息,因?yàn)?#34;base64"算法不是一個(gè)加密算法,它是公開(kāi)的,每個(gè)人都能使用
- 通過(guò)密鑰與加密算法去驗(yàn)證Token令牌的合法性
(2)JWT令牌的使用
(借助工具:"JAVA-JWT")
(3)尾言
下篇博客就要開(kāi)始繼續(xù)完成用戶登錄認(rèn)證的接口開(kāi)發(fā)了!也就是把令牌的生成與驗(yàn)證加入到登錄功能中。