網(wǎng)頁(yè)版夢(mèng)幻西游大鬧天宮困難北京網(wǎng)站優(yōu)化合作
這里寫(xiě)目錄標(biāo)題
- 令牌技術(shù)
- 2.4.1 JWT令牌
- 2.4.2 jwt使用
令牌技術(shù)
令牌,其實(shí)它就是一個(gè)用戶身份的標(biāo)識(shí),其實(shí)本質(zhì)就是一個(gè)字符串。
如果通過(guò)令牌技術(shù)來(lái)跟蹤會(huì)話,就可以在瀏覽器發(fā)起請(qǐng)求。在請(qǐng)求登錄接口的時(shí)候,如果登錄成功,可以生成一個(gè)令牌,令牌就是用戶的合法身份憑證。接下來(lái)響應(yīng)數(shù)據(jù)的時(shí)候,可以直接將令牌響應(yīng)給前端。
接下來(lái)在前端程序當(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ō)明用戶已經(jīng)執(zhí)行了登錄操作,如果令牌是無(wú)效的,就說(shuō)明用戶之前并未執(zhí)行登錄操作。
此時(shí),如果是在同一次會(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))
2.4.1 JWT令牌
JWT全稱:JSON Web Token (官網(wǎng):https://jwt.io/)
- 定義了一種簡(jiǎn)潔的、自包含的格式,用于在通信雙方以json數(shù)據(jù)格式安全的傳輸信息。由于數(shù)字簽名的存在,這些信息是可靠的。
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)。
其實(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)求的處理。
2.4.2 jwt使用
引入依賴
<!--jwt令牌--><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version></dependency>
生成jwt
// 生成jwt@Testpublic void generateToken() {// JWT頭部分信息【Header】Map<String, Object> header = new HashMap<>();header.put("alg", "HS256");header.put("typ", "JWT");// 載核【Payload】Map<String, Object> payload = new HashMap<>();payload.put("sub", "1234567890");payload.put("name", "kwh");payload.put("admin", true);// 聲明Token失效時(shí)間Calendar instance = Calendar.getInstance();instance.add(Calendar.SECOND, 300);// 300s// 生成TokenString token = Jwts.builder().setHeader(header)// 設(shè)置Header.setClaims(payload) // 設(shè)置載核.setExpiration(new Date(System.currentTimeMillis() +3600 *1000))// 設(shè)置生效時(shí)間.signWith(SignatureAlgorithm.HS256, "secret") // 簽名,這里采用私鑰進(jìn)行簽名.compact(); // 壓縮生成xxx.xxx.xxxSystem.out.println(token);}
解析jwt令牌
// 解析jwt令牌@Testpublic void getInfoByJwt() {// 生成的tokenString token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImV4cCI6MTczMDcyNjM2M30.HjTmlXF0gdli6yD-1ZL95FOl8LJOwxRvVTycB8Elyu4";// 解析head信息JwsHeader jwsHeader = Jwts.parser().setSigningKey("secret").parseClaimsJws(token).getHeader();// {typ=JWT, alg=HS256}System.out.println(jwsHeader);System.out.println("1111111");//typ:JWTSystem.out.println("typ:"+jwsHeader.get("typ"));// 解析PayloadClaims claims = Jwts.parser().setSigningKey("secret").parseClaimsJws(token).getBody();System.out.println(claims);// {sub=1234567890, name=John Doe, admin=true, exp=1663297431}System.out.println("2222222");//admin:trueSystem.out.println("admin:"+claims.get("admin"));// 解析SignatureString signature =Jwts.parser().setSigningKey("secret")//指定簽名密鑰.parseClaimsJws(token)//解析令牌.getSignature();System.out.println(signature); //Ju5EzKBpUnuIRhDG1SU0NwMGsd9Jl_8YBcMM6PB2C20}
登錄示例代碼:
utlis包下有JwtUtils工具類
public class JwtUtils {private static String signKey = "secret";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)).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).getBody();return claims;}
}
@PostMapping("/login")public Result login(@RequestBody Emp emp) {log.info("員工登錄,{}", emp);Emp e = empService.login(emp);if (e != null){Map<String, Object> claims = new HashMap<>();claims.put("id",e.getId());claims.put("name",e.getName());claims.put("username",e.getUsername());//jwt包含了當(dāng)前登錄的員工信息String jwt = JwtUtils.generateJwt(claims);return Result.success(jwt);//返回給前端}return Result.error("用戶或密碼錯(cuò)誤!!!!!!!!");}
//jwt包含了當(dāng)前登錄的員工信息String jwt = JwtUtils.generateJwt(claims);return Result.success(jwt);//返回給前端}return Result.error("用戶或密碼錯(cuò)誤!!!!!!!!");
}