登录token存入redis

This commit is contained in:
litianxiang
2025-12-22 11:35:38 +08:00
parent 6429288fd9
commit f84935d0bd
3 changed files with 76 additions and 12 deletions

View File

@@ -6,6 +6,7 @@ import com.ai.da.common.context.UserContext;
import com.ai.da.common.security.config.SecurityProperties; import com.ai.da.common.security.config.SecurityProperties;
import com.ai.da.common.security.jwt.JWTTokenHelper; import com.ai.da.common.security.jwt.JWTTokenHelper;
import com.ai.da.common.utils.LocalCacheUtils; import com.ai.da.common.utils.LocalCacheUtils;
import com.ai.da.common.utils.RedisUtil;
import com.ai.da.common.utils.MultiReadHttpServletRequest; import com.ai.da.common.utils.MultiReadHttpServletRequest;
import com.ai.da.common.utils.MultiReadHttpServletResponse; import com.ai.da.common.utils.MultiReadHttpServletResponse;
import com.ai.da.common.utils.RequestInfoUtil; import com.ai.da.common.utils.RequestInfoUtil;
@@ -40,6 +41,8 @@ public class AuthenticationFilter extends OncePerRequestFilter {
private JWTTokenHelper jwtTokenHelper; private JWTTokenHelper jwtTokenHelper;
@Resource @Resource
private SecurityProperties properties; private SecurityProperties properties;
@Resource
private RedisUtil redisUtil;
private static final List<String> FILTER_URL = private static final List<String> FILTER_URL =
Arrays.asList("/favicon.ico", "/doc.html", "/swagger-ui.html", Arrays.asList("/favicon.ico", "/doc.html", "/swagger-ui.html",
@@ -132,12 +135,19 @@ public class AuthenticationFilter extends OncePerRequestFilter {
UserContext.delete(); UserContext.delete();
//存取用户信息到缓存 //存取用户信息到缓存
UserContext.setUserHolder(principal); UserContext.setUserHolder(principal);
//校验token // 校验 token:先查本地缓存,再查 Redis保证服务重启后仍然有效
String cacheToken = LocalCacheUtils.getTokenCache(String.valueOf(principal.getId())); String userIdStr = String.valueOf(principal.getId());
String cacheToken = LocalCacheUtils.getTokenCache(userIdStr);
if(StringUtils.isEmpty(cacheToken)){ if (StringUtils.isEmpty(cacheToken)) {
// throw new RuntimeException("TOKEN已过期请重新登录"); // 本地缓存为空时,尝试从 Redis 读取
throw new TokenMissingOrExpiredException("TOKEN已过期请重新登录(local cache empty)"); cacheToken = redisUtil.getLoginToken(principal.getId());
if (StringUtils.isEmpty(cacheToken)) {
// throw new RuntimeException("TOKEN已过期请重新登录");
throw new TokenMissingOrExpiredException("TOKEN已过期请重新登录(cache & redis empty)");
}
// 将 Redis 中的 token 回填到本地缓存,减少后续 Redis 访问
LocalCacheUtils.setTokenCache(userIdStr, cacheToken);
} }
if(!cacheToken.equals(jwtToken) ){ if(!cacheToken.equals(jwtToken) ){
// throw new RuntimeException("TOKEN已过期请重新登录"); // throw new RuntimeException("TOKEN已过期请重新登录");

View File

@@ -34,6 +34,11 @@ public class RedisUtil {
private RedisTemplate<String, String> redisTemplate; private RedisTemplate<String, String> redisTemplate;
public final static String FLUX_POLLING_URL = "Flux:"; public final static String FLUX_POLLING_URL = "Flux:";
/**
* 登录 token 在 Redis 中的前缀:
* 最终 key 结构为 login:token:{userId}
*/
public final static String LOGIN_TOKEN_KEY = "login:token:";
public Boolean hasKey(String key){ public Boolean hasKey(String key){
return redisTemplate.hasKey(key); return redisTemplate.hasKey(key);
@@ -186,6 +191,40 @@ public class RedisUtil {
redisTemplate.delete(key); redisTemplate.delete(key);
} }
/**
* 保存登录 token
*
* @param userId 用户 ID
* @param token token 字符串
* @param expireMillis 过期时间(毫秒,通常与 JWT 保持一致)
*/
public void setLoginToken(Long userId, String token, long expireMillis) {
if (expireMillis <= 0) {
// 不设置过期时间,直到手动删除(不推荐)
addToString(LOGIN_TOKEN_KEY + userId, token);
return;
}
long expireSeconds = expireMillis / 1000;
if (expireSeconds <= 0) {
expireSeconds = 1;
}
addToString(LOGIN_TOKEN_KEY + userId, token, expireSeconds);
}
/**
* 获取登录 token
*/
public String getLoginToken(Long userId) {
return getFromString(LOGIN_TOKEN_KEY + userId);
}
/**
* 删除登录 token
*/
public void deleteLoginToken(Long userId) {
removeFromString(LOGIN_TOKEN_KEY + userId);
}
public final static String PORTFOLIO_LIKE_KEY = "portfolio:like:"; public final static String PORTFOLIO_LIKE_KEY = "portfolio:like:";
public void likePost(Long portfolioId, Long userId) { public void likePost(Long portfolioId, Long userId) {

View File

@@ -132,6 +132,9 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
@Resource @Resource
private RedisUtil redisUtil; private RedisUtil redisUtil;
@Resource
private com.ai.da.common.security.config.SecurityProperties securityProperties;
@Resource @Resource
private UserFollowService userFollowService; private UserFollowService userFollowService;
@@ -344,7 +347,11 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
principal.setLanguage(account.getLanguage()); principal.setLanguage(account.getLanguage());
principal.setCountry(account.getCountry()); principal.setCountry(account.getCountry());
String token2 = jwtTokenHelper.createToken(principal); String token2 = jwtTokenHelper.createToken(principal);
// 本地 JVM 缓存(适配旧逻辑)
LocalCacheUtils.setTokenCache(String.valueOf(account.getId()), token2); LocalCacheUtils.setTokenCache(String.valueOf(account.getId()), token2);
// 同步写入 Redis重启后仍然可用
long jwtExpiration = securityProperties.getJwtExpiration();
redisUtil.setLoginToken(account.getId(), token2, jwtExpiration);
return token2; return token2;
} }
@@ -600,21 +607,25 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
@Override @Override
public Boolean logout(AccountLogoutDTO accountLogoutDTO) { public Boolean logout(AccountLogoutDTO accountLogoutDTO) {
//jwt本身失效比较难做 统一用缓存实现 删除缓存失效 // jwt 本身失效比较难做统一用缓存实现删除缓存失效
String token = LocalCacheUtils.getTokenCache(String.valueOf(accountLogoutDTO.getUserId())); String userIdStr = String.valueOf(accountLogoutDTO.getUserId());
if (StringUtils.isNotBlank(token)) { LocalCacheUtils.delTokenCache(userIdStr);
LocalCacheUtils.delTokenCache(String.valueOf(accountLogoutDTO.getUserId())); // 同时删除 Redis 中的 token防止服务重启后仍然有效
} redisUtil.deleteLoginToken(accountLogoutDTO.getUserId());
return Boolean.TRUE; return Boolean.TRUE;
} }
@Override @Override
public Boolean isLogin(AccountLogoutDTO accountLogoutDTO) { public Boolean isLogin(AccountLogoutDTO accountLogoutDTO) {
String token = LocalCacheUtils.getTokenCache(String.valueOf(accountLogoutDTO.getUserId())); String userIdStr = String.valueOf(accountLogoutDTO.getUserId());
// 先查本地缓存
String token = LocalCacheUtils.getTokenCache(userIdStr);
if (StringUtils.isNotBlank(token)) { if (StringUtils.isNotBlank(token)) {
return Boolean.TRUE; return Boolean.TRUE;
} }
return Boolean.FALSE; // 本地没有时,再查 Redis保证服务重启后也能判断登录状态
String redisToken = redisUtil.getLoginToken(accountLogoutDTO.getUserId());
return StringUtils.isNotBlank(redisToken);
} }
@Override @Override
@@ -812,6 +823,8 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
if (StringUtils.isNotBlank(token)) { if (StringUtils.isNotBlank(token)) {
LocalCacheUtils.delTokenCache(String.valueOf(accountDelete.getId())); LocalCacheUtils.delTokenCache(String.valueOf(accountDelete.getId()));
} }
// 删除 Redis 中对应的登录 token
redisUtil.deleteLoginToken(accountDelete.getId());
if (!userName.equals(userToBeUpdate.getUserName())) { if (!userName.equals(userToBeUpdate.getUserName())) {
// accountMapper.deleteById(accountDelete); // accountMapper.deleteById(accountDelete);
log.info("排查用户被删除原因deleteNoLoginRequiredtrue, 删除用户(改为降为游客)"); log.info("排查用户被删除原因deleteNoLoginRequiredtrue, 删除用户(改为降为游客)");
@@ -1065,6 +1078,8 @@ public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> impl
if (StringUtils.isNotBlank(token)) { if (StringUtils.isNotBlank(token)) {
LocalCacheUtils.delTokenCache(String.valueOf(account.getId())); LocalCacheUtils.delTokenCache(String.valueOf(account.getId()));
} }
// 删除 Redis 中对应的登录 token
redisUtil.deleteLoginToken(account.getId());
// accountMapper.deleteById(account.getId()); // accountMapper.deleteById(account.getId());
log.info("排查用户被删除原因deleteNoLoginRequiredNew删除用户改为降为游客"); log.info("排查用户被删除原因deleteNoLoginRequiredNew删除用户改为降为游客");
accountMapper.toVisitor(account.getId()); accountMapper.toVisitor(account.getId());