通過JWT來解決登錄認(rèn)證問題的方案
1. 問題引入
在登錄功能的實(shí)現(xiàn)中
傳統(tǒng)思路:
登錄頁(yè)面時(shí)把用戶名和密碼提交給服務(wù)器服務(wù)器驗(yàn)證用戶名和密碼,并把檢驗(yàn)結(jié)果返回給后端如果密碼正確,則在服務(wù)器端創(chuàng)建 session,通過 cookie 把 session id 返回給瀏覽器

但是正常情況下一個(gè) web 應(yīng)用是部署到多個(gè)服務(wù)器上的,通過 Nginx 等進(jìn)行負(fù)載均衡,此時(shí)就可能出現(xiàn)這樣的情況:用戶登錄請(qǐng)求之后把 session 存儲(chǔ)在了第一臺(tái)服務(wù)器上,但是后續(xù)的請(qǐng)求操作,例如查詢等,就可能會(huì)轉(zhuǎn)發(fā)到第二臺(tái)服務(wù)器上,但是第二臺(tái)服務(wù)器沒有存儲(chǔ)該用戶的 session,就會(huì)讓用戶重新登錄,這肯定是不合理的

解決方案:
對(duì)于服務(wù)端來說,上述出現(xiàn)的問題是由于 session 是默認(rèn)存儲(chǔ)在內(nèi)存中的,服務(wù)器重啟之后,session 就丟失了,如果把 session 存儲(chǔ)在 Redis 中,那么就能共同訪問,并且不丟失數(shù)據(jù)。第二種方案就是引入 token,也就是令牌,用戶登錄之后,服務(wù)器對(duì)賬號(hào)和密碼進(jìn)行驗(yàn)證,驗(yàn)證通過就生成一個(gè)令牌,并返回給客戶端,客戶端收到令牌之后,把令牌存儲(chǔ)起來,之后再發(fā)起其他請(qǐng)求就帶著令牌,處理請(qǐng)求的服務(wù)器校驗(yàn)令牌是否有效即可

引入令牌之后就解決了集群環(huán)境下的認(rèn)證問題,并且減輕了服務(wù)器的存儲(chǔ)壓力,令牌由客戶端存儲(chǔ),服務(wù)器只負(fù)責(zé)生成和校驗(yàn)
2. JWT 的介紹
官網(wǎng):JSON Web Tokens - jwt.io
JWT 令牌本身是一個(gè)字符串,包括頭部,載荷,簽名三部分,將信息作為 JSON 對(duì)象進(jìn)行傳輸

頭部:包括令牌的類型和使用的哈希算法
載荷:存儲(chǔ)的有效信息,為自定義內(nèi)容
簽名:用于防止 JWT 內(nèi)容被篡改(并不是防止被解析),只要被篡改,令牌就會(huì)失效
3. JWT 的使用
首先需要導(dǎo)入對(duì)應(yīng)的依賴:
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>0.11.5</version> </dependency> <!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-impl --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <version>0.11.5</version> <scope>runtime</scope> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if Gson is preferred --> <version>0.11.5</version> <scope>runtime</scope> </dependency>
接下來就可以測(cè)試生成 token 了
//生成token
@Test
public void getToken() {
String secret = "abcdefghijklmnopqrstuvwxyz";
//設(shè)置key,用于簽名
Key key = Keys.hmacShaKeyFor(Decoders.BASE64.decode(secret));
//載荷
Map<String, Object> map = new HashMap<>();
map.put("name", "zhangsan");
map.put("id", 1);
//生成token
String compact = Jwts.builder().setClaims(map).signWith(key).compact();
System.out.println(compact);
}此時(shí)報(bào)出了一個(gè)錯(cuò)誤,要求使用提供的方法來生成 key

接下來看怎么生成 key
@Test
public void genKey(){
//生成key
SecretKey secretKey = Keys.secretKeyFor(SignatureAlgorithm.HS256);
//轉(zhuǎn)化為String類型
String enconde = Encoders.BASE64.encode(secretKey.getEncoded());
System.out.println(enconde);
}
生成之后就可以替換掉原來自定義的字符串了,再去生成 token

在官網(wǎng)中也是可以校驗(yàn)成功的

接下來看怎么通過方法來進(jìn)行 token 的校驗(yàn):
//校驗(yàn)token
@Test
public void parseToken(){
String token = "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiemhhbmdzYW4iLCJpZCI6MX0.xllreml0yt9aQDXSQe0ngQb45VpV5843rOEKdDQ4QCk";
//JWT解析器
JwtParser build = Jwts.parserBuilder().setSigningKey(key).build();
//對(duì)創(chuàng)建好的token進(jìn)行解析
Object body = build.parse(token).getBody();
System.out.println(body);
}
如果說簽名錯(cuò)了就無法正確解析了:

這就可以通過 try- catch 進(jìn)行邏輯處理了:

根據(jù)這些就可以寫一個(gè)工具類,服務(wù)端就可以直接調(diào)用了
@Slf4j
public class JwtUtil {
//設(shè)置key,用于簽名
private final static String secret = "WHMgtn1tTrIxc00ys17ukp65bf2KZ0wrihyqynY18F8=sssss";
private final static Key key = Keys.hmacShaKeyFor(Decoders.BASE64.decode(secret));
private final static long expiration = 24 * 60 * 60 * 1000;
//生成token
public static String getToken(Map<String, Object> map) {
return Jwts.builder()
.setClaims(map)
.setExpiration(new Date(System.currentTimeMillis() + expiration))//設(shè)置過期時(shí)間
.setIssuedAt(new Date()) //設(shè)置簽發(fā)日期
.signWith(key)
.compact();
}
//校驗(yàn)token
public static Claims parseToken(String token) {
if (!StringUtils.hasLength(token)) {
return null;
}
//JWT解析器
JwtParser build = Jwts.parserBuilder().setSigningKey(key).build();
//對(duì)創(chuàng)建好的token進(jìn)行解析
Claims body = null;
try {
body = build.parseClaimsJws(token).getBody();
return body;
} catch (SignatureException e) {
log.error("token非法...e{}", e.getMessage());
} catch (ExpiredJwtException e) {
log.error("token過期... e{}", e.getMessage());
} catch (Exception e) {
log.error("token解析失敗,e{}", e.getMessage());
}
return body;
}
}以上就是通過JWT來解決登錄認(rèn)證問題的方案的詳細(xì)內(nèi)容,更多關(guān)于JWT登錄認(rèn)證問題的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Spring之什么是ObjectFactory?什么是ObjectProvider?
這篇文章主要介紹了Spring之什么是ObjectFactory?什么是ObjectProvider?具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-01-01
Spring Boot 部署過程解析(jar or war)
這篇文章主要介紹了Spring Boot 部署過程解析(jar or war),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-09-09
JDK動(dòng)態(tài)代理與CGLib動(dòng)態(tài)代理的區(qū)別對(duì)比
今天小編就為大家分享一篇關(guān)于JDK動(dòng)態(tài)代理與CGLib動(dòng)態(tài)代理的區(qū)別對(duì)比,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-02-02
spring注解 @PropertySource配置數(shù)據(jù)源全流程
這篇文章主要介紹了spring注解 @PropertySource配置數(shù)據(jù)源全流程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03

