feat: 更新 JWT 黑名单服务,支持令牌过期时间管理并添加定时清理功能
This commit is contained in:
parent
400a06f5a3
commit
244f0e7667
@ -2,8 +2,10 @@ package com.userauth.restuserauth;
|
|||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
|
@EnableScheduling
|
||||||
public class RestUserAuthApplication {
|
public class RestUserAuthApplication {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.userauth.restuserauth.controller;
|
package com.userauth.restuserauth.controller;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@ -85,8 +86,10 @@ public class AuthController {
|
|||||||
String bearerToken = request.getHeader("Authorization");
|
String bearerToken = request.getHeader("Authorization");
|
||||||
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
|
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
|
||||||
String jwt = bearerToken.substring(7);
|
String jwt = bearerToken.substring(7);
|
||||||
// 将当前 JWT 加入黑名单
|
// 获取令牌的过期时间
|
||||||
jwtBlacklistService.blacklistToken(jwt);
|
Date expirationDate = tokenProvider.getExpirationDateFromToken(jwt);
|
||||||
|
// 将当前JWT及其过期时间加入黑名单
|
||||||
|
jwtBlacklistService.blacklistToken(jwt, expirationDate);
|
||||||
logger.info("令牌已加入黑名单:{}", jwt);
|
logger.info("令牌已加入黑名单:{}", jwt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,23 +4,28 @@ import java.util.Date;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class JwtBlacklistService {
|
public class JwtBlacklistService {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(JwtBlacklistService.class);
|
||||||
|
|
||||||
// 使用 ConcurrentHashMap 作为内存中的黑名单存储。
|
// 使用 ConcurrentHashMap 作为内存中的黑名单存储。
|
||||||
// 在生产环境中,建议使用 Redis 等分布式缓存来支持多实例部署。
|
|
||||||
private final Map<String, Date> blacklist = new ConcurrentHashMap<>();
|
private final Map<String, Date> blacklist = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将令牌加入黑名单。
|
* 将令牌及其过期时间加入黑名单。
|
||||||
* @param token 要加入黑名单的 JWT。
|
* @param token 要加入黑名单的 JWT。
|
||||||
|
* @param expirationDate 令牌的过期时间。
|
||||||
*/
|
*/
|
||||||
public void blacklistToken(String token) {
|
public void blacklistToken(String token, Date expirationDate) {
|
||||||
// 为了简化,我们暂时不处理过期清理,直接加入。
|
if (token != null && expirationDate != null) {
|
||||||
// 在实际应用中,可以存储令牌的过期时间,并定期清理已过期的令牌以防止内存泄漏。
|
blacklist.put(token, expirationDate);
|
||||||
blacklist.put(token, new Date());
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -31,4 +36,18 @@ public class JwtBlacklistService {
|
|||||||
public boolean isBlacklisted(String token) {
|
public boolean isBlacklisted(String token) {
|
||||||
return blacklist.containsKey(token);
|
return blacklist.containsKey(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 定时任务,定期清理黑名单中已过期的令牌。
|
||||||
|
* cron 表达式 "0 0 * * * *" 表示每小时的 0 分 0 秒执行一次。
|
||||||
|
*/
|
||||||
|
@Scheduled(cron = "*/30 * * * * *")
|
||||||
|
public void cleanupExpiredTokens() {
|
||||||
|
logger.info("开始清理黑名单中的过期令牌... 当前黑名单大小:{}", blacklist.size());
|
||||||
|
|
||||||
|
// 使用 removeIf 安全地从 ConcurrentHashMap 中移除过期的条目
|
||||||
|
blacklist.entrySet().removeIf(entry -> entry.getValue().before(new Date()));
|
||||||
|
|
||||||
|
logger.info("清理完成。当前黑名单大小:{}", blacklist.size());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,4 +61,14 @@ public class JwtTokenProvider {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 新增:从 JWT 中获取过期时间
|
||||||
|
public Date getExpirationDateFromToken(String token) {
|
||||||
|
Claims claims = Jwts.parserBuilder()
|
||||||
|
.setSigningKey(key)
|
||||||
|
.build()
|
||||||
|
.parseClaimsJws(token)
|
||||||
|
.getBody();
|
||||||
|
return claims.getExpiration();
|
||||||
|
}
|
||||||
}
|
}
|
@ -27,4 +27,4 @@ spring.jpa.properties.hibernate.format_sql=true
|
|||||||
# 用于签发 JWT 的密钥。请务必修改为一个足够长且复杂的字符串,不要在生产环境中泄露
|
# 用于签发 JWT 的密钥。请务必修改为一个足够长且复杂的字符串,不要在生产环境中泄露
|
||||||
app.jwt.secret=YourSuperSecretKeyForJWTsWhichIsLongAndSecureAbcdAbcdAbcdAbcdAbcdAbcdAbcdAbcdAbcdAbcd
|
app.jwt.secret=YourSuperSecretKeyForJWTsWhichIsLongAndSecureAbcdAbcdAbcdAbcdAbcdAbcdAbcdAbcdAbcdAbcd
|
||||||
# JWT 的过期时间(毫秒),这里设置为 1 小时
|
# JWT 的过期时间(毫秒),这里设置为 1 小时
|
||||||
app.jwt.expiration-ms=3600000
|
app.jwt.expiration-ms=60000
|
||||||
|
@ -20,9 +20,9 @@ Content-Type: application/json
|
|||||||
|
|
||||||
### Get user info
|
### Get user info
|
||||||
GET http://localhost:8080/api/user/me
|
GET http://localhost:8080/api/user/me
|
||||||
Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ0ZXN0dXNlcjAyIiwiaWF0IjoxNzQ5MTk2ODQxLCJleHAiOjE3NDkyMDA0NDF9.BM9ZeCP8Xbesk8hQj04Rr4EMRQ84fpjX9ikk8yIUPvF0mS5pVoR5J_bEJ1It5C6UkFteq0v8VVK9nWHDMfIEGg
|
Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ0ZXN0dXNlcjAyIiwiaWF0IjoxNzQ5MTk3NzEzLCJleHAiOjE3NDkxOTc3NzN9.34iPSN77CJ2yrOp9pxCBguNkEJqu8VTgrIb7oWPQh1yTQQVcHMxQuMlMolorUOT216BF0_7b9vWS_COONQzGQA
|
||||||
|
|
||||||
### Logout
|
### Logout
|
||||||
POST http://localhost:8080/api/auth/logout
|
POST http://localhost:8080/api/auth/logout
|
||||||
Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ0ZXN0dXNlcjAyIiwiaWF0IjoxNzQ5MTk2ODQxLCJleHAiOjE3NDkyMDA0NDF9.BM9ZeCP8Xbesk8hQj04Rr4EMRQ84fpjX9ikk8yIUPvF0mS5pVoR5J_bEJ1It5C6UkFteq0v8VVK9nWHDMfIEGg
|
Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ0ZXN0dXNlcjAyIiwiaWF0IjoxNzQ5MTk3NzEzLCJleHAiOjE3NDkxOTc3NzN9.34iPSN77CJ2yrOp9pxCBguNkEJqu8VTgrIb7oWPQh1yTQQVcHMxQuMlMolorUOT216BF0_7b9vWS_COONQzGQA
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user