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