網站首頁 編程語言 正文
應用場景
當用戶登錄狀態到登出狀態時,對應的JWT的令牌需要設置為失效狀態,這時可以使用基于 Redis 的黑名單方案來實現JWT令牌失效。
基于 Redis 的黑名單方案
當用戶需要登出系統時,將用戶攜帶的Token進行解析,解碼出JWT令牌,取出對應的 UUID 和 過期時間 ,用過期的時間減去當前的時間,計算出這個Key的過期時間,再以這兩個字段拼接作為 Key 并設置好過期時間存儲到 Redis 中,如果有黑客拿竊取出來的JWT令牌進行登錄,只要判斷這個JWT令牌是否在黑名單就可以。
實現步驟
1.獲得攜帶的Token解析并取出JWT令牌的代碼
這段代碼實現了對指定 JWT 的驗證和使令牌失效的操作。
- 首先,通過調用 convertToken(headerToken) 方法將傳入的頭部令牌 headerToken 轉換成實際的 JWT 字符串 token。
- 然后,使用 HMAC256 算法和預設的密鑰 key 創建一個算法實例 algorithm。
- 接下來,使用算法實例 algorithm 構建一個 JWT 驗證器 jwtVerifier。這個驗證器將用于驗證 JWT 的有效性。
- 在 try-catch 塊中,首先通過調用 jwtVerifier.verify(token) 方法對 JWT 進行驗證。如果驗證成功,則返回一個 DecodedJWT 對象 verify,其中包含了 JWT 的解碼信息,如令牌的唯一標識符(ID)和過期時間等。
- 接著,調用 deleteToken(verify.getId(), verify.getExpiresAt()) 方法來刪除指定令牌,并將其加入到黑名單中進行失效處理。這里使用了 verify 對象中的 ID 和過期時間作為參數。
- 最后,如果在驗證 JWT 過程中發生了 JWTVerificationException 異常,即 JWT 驗證失敗,則捕獲該異常,并返回 false 表示令牌失效操作失敗。
/**
* 讓指定Jwt令牌失效
* @param headerToken 請求頭中攜帶的令牌
* @return 是否操作成功
*/
public boolean invalidateJwt(String headerToken){
String token = this.convertToken(headerToken);
Algorithm algorithm = Algorithm.HMAC256(key);
JWTVerifier jwtVerifier = JWT.require(algorithm).build();
try {
DecodedJWT verify = jwtVerifier.verify(token);
return deleteToken(verify.getId(), verify.getExpiresAt());
} catch (JWTVerificationException e) {
return false;
}
}
2.檢查指定 UUID 的令牌是否為無效的(已加入黑名單)
這段代碼用于檢查指定 UUID 的令牌是否為無效的(已加入黑名單),通過判斷 Redis 數據庫中是否存在相應的鍵來決定令牌的有效性。如果鍵存在,則表示令牌已失效;如果鍵不存在,則表示令牌仍然有效。
/**
* 驗證Token是否被列入Redis黑名單
* @param uuid 令牌ID
* @return 是否操作成功
*/
private boolean isInvalidToken(String uuid){
return Boolean.TRUE.equals(template.hasKey(Const.JWT_BLACK_LIST + uuid));
}
3.將Token列入Redis黑名單中
這段代碼實現了對指定令牌的刪除和加入黑名單的操作,用于管理令牌的有效性和安全性。
- 首先,通過調用 isInvalidToken(uuid) 方法來檢查指定的 UUID 是否為無效的令牌。如果 isInvalidToken 方法返回 true,則說明該令牌無效,此時直接返回 false,不執行后續操作。
- 獲取當前時間 now,然后計算令牌的過期時間與當前時間的差值,并取最大值作為令牌的失效時間 expire。這里使用了 Math.max 方法來確保失效時間不會小于 0。
- 最后,通過 Redis 的 template 對象調用 opsForValue().set() 方法,將指定 UUID 的令牌加入到名為 Const.JWT_BLACK_LIST + uuid 的鍵中,并設置過期時間為 expire 毫秒。這樣就將該令牌加入到了黑名單中,使其在一定時間后失效。
- 最終,方法返回 true 表示成功刪除令牌并將其加入黑名單。
/**
* 將Token列入Redis黑名單中
* @param uuid 令牌ID
* @param time 過期時間
* @return 是否操作成功
*/
private boolean deleteToken(String uuid, Date time){
if(this.isInvalidToken(uuid))
return false;
Date now = new Date();
long expire = Math.max(time.getTime() - now.getTime(), 0);
template.opsForValue().set(Const.JWT_BLACK_LIST + uuid, "", expire, TimeUnit.MILLISECONDS);
return true;
}
public final class Const {
//JWT令牌
public final static String JWT_BLACK_LIST = "jwt:blacklist:";
public final static String JWT_FREQUENCY = "jwt:frequency:";
}
對應完整的代碼如下:
@Component
public class JwtUtils {
@Autowired
private StringRedisTemplate template;
@Value("${spring.security.jwt.key}")
String key;
@Value("${spring.security.jwt.expire}")
int expire;
/**
* 讓指定Jwt令牌失效
* @param headerToken 請求頭中攜帶的令牌
* @return 是否操作成功
*/
public boolean invalidateJwt(String headerToken){
String token = this.convertToken(headerToken);
Algorithm algorithm = Algorithm.HMAC256(key);
JWTVerifier jwtVerifier = JWT.require(algorithm).build();
try {
DecodedJWT verify = jwtVerifier.verify(token);
return deleteToken(verify.getId(), verify.getExpiresAt());
} catch (JWTVerificationException e) {
return false;
}
}
/**
* 將Token列入Redis黑名單中
* @param uuid 令牌ID
* @param time 過期時間
* @return 是否操作成功
*/
private boolean deleteToken(String uuid, Date time){
if(this.isInvalidToken(uuid))
return false;
Date now = new Date();
long expire = Math.max(time.getTime() - now.getTime(), 0);
template.opsForValue().set(Const.JWT_BLACK_LIST + uuid, "", expire, TimeUnit.MILLISECONDS);
return true;
}
/**
* 驗證Token是否被列入Redis黑名單
* @param uuid 令牌ID
* @return 是否操作成功
*/
private boolean isInvalidToken(String uuid){
return Boolean.TRUE.equals(template.hasKey(Const.JWT_BLACK_LIST + uuid));
}
public DecodedJWT resolveJwt(String headerToken) {
String token = this.convertToken(headerToken);
if (token == null) {
return null;
}
Algorithm algorithm = Algorithm.HMAC256(key);
JWTVerifier jwtVerifier = JWT.require(algorithm).build();
try {
DecodedJWT verify = jwtVerifier.verify(token);
if(this.isInvalidToken(verify.getId())) return null;
Date expireAt = verify.getExpiresAt();
return new Date().after(expireAt) ? null : verify;
} catch (JWTVerificationException e) {
return null;
}
}
public UserDetails toUser(DecodedJWT jwt) {
Map<String, Claim> claims = jwt.getClaims();
return User.withUsername(claims.get("name").asString())
.password("********")
.authorities(claims.get("authorities").asArray(String.class))
.build();
}
public Integer toId(DecodedJWT jwt) {
Map<String, Claim> claims = jwt.getClaims();
return claims.get("id").asInt();
}
public String createJwt(UserDetails details, int id, String username) {
Algorithm algorithm = Algorithm.HMAC256(key);
Date expire = this.expireTime();
return JWT.create()
.withJWTId(UUID.randomUUID().toString())
.withClaim("id", id)
.withClaim("name", username)
.withClaim("authorities", details.getAuthorities().stream().map(GrantedAuthority::getAuthority).toList())
.withExpiresAt(expire)
.withIssuedAt(new Date())
.sign(algorithm);
}
public Date expireTime() {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.HOUR, expire * 24);
return calendar.getTime();
}
private String convertToken(String headerToken) {
if(headerToken == null || !headerToken.startsWith("Bearer ")) {
return null;
}
return headerToken.substring(7);
}
}
原文鏈接:https://blog.csdn.net/Mr_VK/article/details/136456365
- 上一篇:沒有了
- 下一篇:沒有了
相關推薦
- 2022-04-25 C#實現簡單串口通信_C#教程
- 2022-09-03 .NET使用System.Timers.Timer類實現程序定時執行_實用技巧
- 2022-12-11 React高級特性Context萬字詳細解讀_React
- 2022-02-28 ERROR in Entry module not found: Error: Can't reso
- 2022-06-13 Docker鏡像的commit操作示例及作用_docker
- 2022-05-12 ubuntu 20.04 redis fatal error: stdlib.h: No such
- 2022-06-10 FreeRTOS實時操作系統隊列基礎_操作系統
- 2023-04-06 C++聚合體初始化aggregate?initialization詳細介紹_C 語言
- 欄目分類
-
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支