Java生成及校驗(yàn)token的實(shí)踐
What is token?
token是令牌的意思,作用就像“通關(guān)令牌”一樣,持有token的請求會(huì)被“放行”,不持有token的請求可以被攔截(可以設(shè)置白名單使不被攔截,例如登陸請求)。
token是由服務(wù)端創(chuàng)建的一串字符串,登陸成功后發(fā)送給前端存儲(chǔ)在瀏覽器或本地中,以后每次發(fā)送請求都攜帶上。
1. 使用攔截器在請求發(fā)出前在請求頭中添加上 token。

2. 將 token 存儲(chǔ)在瀏覽器的 cookies 中,符合一些條件每次請求都會(huì)自動(dòng)帶上 token。

Token安全問題
- 在存儲(chǔ)的時(shí)候把token進(jìn)行對稱加密存儲(chǔ),用時(shí)解開。
- 將請求URL、時(shí)間戳、token三者進(jìn)行合并加鹽簽名,服務(wù)端校驗(yàn)有效性。
- 這兩種辦法的出發(fā)點(diǎn)都是:竊取你存儲(chǔ)的數(shù)據(jù)較為容易,而反匯編你的程序hack你的加密解密和簽名算法是比較難的。然而其實(shí)說難也不難,所以終究是防君子不防小人的做法。話說加密存儲(chǔ)一個(gè)你要是被人扒開客戶端看也不會(huì)被噴明文存儲(chǔ)……
- 方法1它拿到存儲(chǔ)的密文解不開、方法2它不知道你的簽名算法和鹽,兩者可以結(jié)合食用。
- 但是如果token被人拷走,他自然也能植入到自己的手機(jī)里面,那到時(shí)候他的手機(jī)也可以以你的身份來用著,這你就瞎了。
- 于是可以提供一個(gè)讓用戶可以主動(dòng)expire一個(gè)過去的token類似的機(jī)制,在被盜的時(shí)候能遠(yuǎn)程止損。
- 在網(wǎng)絡(luò)層面上token明文傳輸?shù)脑挄?huì)非常的危險(xiǎn),所以建議一定要使用HTTPS,并且把token放在post body里
Why do we use token?
在前后端分離的web項(xiàng)目中,HTTP是無狀態(tài)協(xié)議,即使使用賬號(hào)密碼登陸,后端仍然無法分辨是誰發(fā)出的請求,要么后端不需要確認(rèn)請求者的身份,要么每次請求都攜帶身份信息供后端確認(rèn)。
顯然第一種方法對軟件的安全會(huì)造成極大的威脅,那么第二種方法就被改善成了token,token就是加密了的用戶身份信息。
token組成(Composition of token)
以jwt(java web token)為例,下圖介紹了詳細(xì)介紹了token的組成。

校驗(yàn)token(Verify token)
這里只說最基礎(chǔ)的校驗(yàn)。
token的加密一般是可逆的,后端接收到token中,還可以根據(jù)加密的算法再進(jìn)行解密,以獲取荷載中的用戶信息,因此荷載中不能放置密碼等信息。
第三部分由于加入了自己制定的秘鑰(秘鑰一般保存在后端代碼中),解密成功后會(huì)與前兩部分和保存的秘鑰進(jìn)行對比,對比成功了才算token驗(yàn)證通過。經(jīng)過這樣的雙重保障,這三部分每一部分被篡改都會(huì)被發(fā)現(xiàn)。
校驗(yàn)流程:

實(shí)操:
生成token(Generate token)
以生成jwt為例子,代碼如下:
<!-- 引入jwt -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.8.2</version>
</dependency>校驗(yàn)token(Verificationtoken)
@Slf4j
public class JwtUtil {
/**
* 使用固定的解密秘鑰
*/
private static final String SECRET = TOKEN_SECRET;
/**
* @version: V1.0
* @description: 生成token并驗(yàn)證token并解密token中的信息
* @param: userInfo 用戶手機(jī)號(hào)和用戶Id
* @return: java.lang.String 返回token
**/
public static String getToken(UserInfoEntity userInfo) {
try{
//用秘鑰生成簽名
Algorithm algorithm = Algorithm.HMAC256(SECRET);
//默認(rèn)頭部+載荷(手機(jī)號(hào)/id)+簽名=jwt
String jwtToken= JWT.create()
.withClaim("userPhone", userInfo.getUserPhone())
.withClaim("userId", userInfo.getUserId())
.sign(algorithm);
log.info("用戶{}的token生成成功:{}",userInfo.getUserId(),jwtToken);
return jwtToken;
}catch (Exception e){
log.error("用戶{}的token生成異常:{}",userInfo.getUserId(),e);
return null;
}
}
/**
* @version: V1.0
* @description: 校驗(yàn)token是否正確
* @param: token
* @param: userPhone
* @return: UserInfoEntity token中的用戶信息(姓名/id)
**/
public static UserInfoEntity verify(String token) {
try {
// 根據(jù)用戶信息userInfo生成JWT效驗(yàn)器
Algorithm algorithm = Algorithm.HMAC256(SECRET);
JWTVerifier verifier = JWT.require(algorithm)
.build();
// 效驗(yàn)TOKEN
verifier.verify(token);
log.info("token:{}校驗(yàn)成功成功",token);
//返回token內(nèi)容
return getTokenInfo(token);
} catch (Exception exception) {
log.error("token校驗(yàn)異常:{}",exception);
return null;
}
}
/**
* @version: V1.0
* @Title: getUsername
* @description: 從Token中解密獲得Token中的用戶信息
* @param: token
* @return: UserInfoEntity token中的用戶信息(姓名/id)
**/
private static UserInfoEntity getTokenInfo(String token) {
try {
DecodedJWT jwt = JWT.decode(token);
UserInfoEntity userInfo=new UserInfoEntity();
userInfo.setUserPhone(jwt.getClaim("userPhone").asString());
userInfo.setUserId(jwt.getClaim("userId").asString());
log.info("用戶{}從token獲取用戶信息成功",userInfo.getUserId());
return userInfo;
} catch (JWTDecodeException e) {
log.error("從token:{}獲取用戶信息異常:{}",token,e);
return null;
}
}
}
總結(jié):
在實(shí)際應(yīng)用中,還應(yīng)考慮安全性措施,如使用安全的隨機(jī)數(shù)生成器生成密鑰、定期更換密鑰、使用 HTTPS 等。
Token 的生成和校驗(yàn)機(jī)制為應(yīng)用程序提供了一種安全的身份驗(yàn)證和授權(quán)方式,可以用于用戶認(rèn)證、API 訪問控制等場景。正確實(shí)現(xiàn)和使用 Token 機(jī)制可以提高應(yīng)用程序的安全性和用戶體驗(yàn)。
到此這篇關(guān)于Java生成及校驗(yàn)token的實(shí)踐的文章就介紹到這了,更多相關(guān)Java生成及校驗(yàn)token內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android中Socket通信的實(shí)現(xiàn)方法概述
這篇文章主要介紹了Android中Socket通信的實(shí)現(xiàn)方法,很有實(shí)用價(jià)值,需要的朋友可以參考下2014-08-08
Java之獲取客戶端真實(shí)IP地址的實(shí)現(xiàn)
在開發(fā)工作中,我們常常需要獲取客戶端的IP,本文主要介紹了Jav之獲取客戶端真實(shí)IP地址的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2023-12-12
Spring?MVC?請求映射路徑的配置實(shí)現(xiàn)前后端交互
在Spring?MVC中,請求映射路徑是指與特定的請求處理方法關(guān)聯(lián)的URL路徑,這篇文章主要介紹了Spring?MVC?請求映射路徑的配置,實(shí)現(xiàn)前后端交互,需要的朋友可以參考下2023-09-09
Spring Boot訪問靜態(tài)資源css/js,你真的懂了嗎
在搭建springboot時(shí)經(jīng)常需要在html中訪問一些靜態(tài)資源,很多朋友不清楚如何在 Spring Boot中訪問靜態(tài)資源,本文給大家?guī)韮煞N解決方案,感興趣的朋友跟隨小編一起看看吧2021-05-05
Mybatis分頁查詢主從表的實(shí)現(xiàn)示例
本文主要介紹了Mybatis分頁查詢主從表的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-09-09

