refactor: 会话失效、数据权限和实时推送重构
This commit is contained in:
@@ -4,7 +4,9 @@ import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONArray;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import cn.hutool.jwt.JWT;
|
||||
import cn.hutool.jwt.JWTPayload;
|
||||
import cn.hutool.jwt.JWTUtil;
|
||||
@@ -15,6 +17,7 @@ import com.youlai.boot.core.exception.BusinessException;
|
||||
import com.youlai.boot.core.web.ResultCode;
|
||||
import com.youlai.boot.config.property.SecurityProperties;
|
||||
import com.youlai.boot.security.model.AuthenticationToken;
|
||||
import com.youlai.boot.security.model.RoleDataScope;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import com.youlai.boot.security.model.SysUserDetails;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
@@ -25,17 +28,20 @@ import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* JWT Token 管理器
|
||||
* <p>
|
||||
* 用于生成、解析、校验、刷新 JWT Token
|
||||
* 实现基于JWT的无状态认证,支持:
|
||||
* <ul>
|
||||
* <li>Access Token + Refresh Token 双令牌机制</li>
|
||||
* <li>Token 撤销(jti黑名单)</li>
|
||||
* <li>用户级会话失效(tokenValidAfter)</li>
|
||||
* <li>多角色数据权限存储</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Ray.Hao
|
||||
* @since 2024/11/15
|
||||
@@ -44,6 +50,9 @@ import java.util.stream.Collectors;
|
||||
@Service
|
||||
public class JwtTokenManager implements TokenManager {
|
||||
|
||||
/** tokenValidAfter 默认过期时间(7天),避免Redis内存泄漏 */
|
||||
private static final long TOKEN_VALID_AFTER_TTL_SECONDS = 7 * 24 * 60 * 60;
|
||||
|
||||
private final SecurityProperties securityProperties;
|
||||
private final RedisTemplate<String, Object> redisTemplate;
|
||||
private final byte[] secretKey;
|
||||
@@ -90,7 +99,25 @@ public class JwtTokenManager implements TokenManager {
|
||||
SysUserDetails userDetails = new SysUserDetails();
|
||||
userDetails.setUserId(payloads.getLong(JwtClaimConstants.USER_ID)); // 用户ID
|
||||
userDetails.setDeptId(payloads.getLong(JwtClaimConstants.DEPT_ID)); // 部门ID
|
||||
userDetails.setDataScope(payloads.getInt(JwtClaimConstants.DATA_SCOPE)); // 数据权限范围
|
||||
|
||||
// 解析数据权限列表
|
||||
JSONArray dataScopesArray = payloads.getJSONArray(JwtClaimConstants.DATA_SCOPES);
|
||||
if (dataScopesArray != null && !dataScopesArray.isEmpty()) {
|
||||
List<RoleDataScope> dataScopes = dataScopesArray.stream()
|
||||
.map(obj -> {
|
||||
JSONObject item = (JSONObject) obj;
|
||||
String roleCode = item.getStr("roleCode");
|
||||
Integer dataScope = item.getInt("dataScope");
|
||||
JSONArray deptIdsArray = item.getJSONArray("customDeptIds");
|
||||
List<Long> customDeptIds = null;
|
||||
if (deptIdsArray != null) {
|
||||
customDeptIds = deptIdsArray.toList(Long.class);
|
||||
}
|
||||
return new RoleDataScope(roleCode, dataScope, customDeptIds);
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
userDetails.setDataScopes(dataScopes);
|
||||
}
|
||||
|
||||
userDetails.setUsername(payloads.getStr(JWTPayload.SUBJECT)); // 用户名
|
||||
// 角色集合
|
||||
@@ -126,9 +153,17 @@ public class JwtTokenManager implements TokenManager {
|
||||
|
||||
/**
|
||||
* 校验令牌
|
||||
* <p>
|
||||
* 校验流程(按顺序执行):
|
||||
* <ol>
|
||||
* <li>签名验证 + 过期时间检查</li>
|
||||
* <li>刷新令牌类型校验(仅刷新场景)</li>
|
||||
* <li>tokenValidAfter 校验(用户级会话失效)</li>
|
||||
* <li>jti 黑名单校验(单Token撤销)</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param token JWT Token
|
||||
* @param validateRefreshToken 是否校验刷新令牌
|
||||
* @param validateRefreshToken 是否校验刷新令牌类型
|
||||
* @return 是否有效
|
||||
*/
|
||||
private boolean validateToken(String token, boolean validateRefreshToken) {
|
||||
@@ -147,27 +182,28 @@ public class JwtTokenManager implements TokenManager {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// 2. 校验安全版本号(用于按用户维度失效历史 Token)
|
||||
// 场景示例:用户修改密码、被管理员强制下线、手动“踢所有端”后,将用户安全版本号 +1,旧版本 Token 全部失效
|
||||
// 2. 校验 tokenValidAfter(用于按用户维度失效历史 Token)
|
||||
// 场景示例:用户修改密码、被管理员强制下线、手动“踢所有端”后,更新 tokenValidAfter,早于该时间签发的 Token 全部失效
|
||||
Long userId = payloads.getLong(JwtClaimConstants.USER_ID);
|
||||
if (userId != null) {
|
||||
// 老版本 Token 可能没有 SECURITY_VERSION 声明,视为 0 版本
|
||||
Integer tokenVersionRaw = payloads.getInt(JwtClaimConstants.SECURITY_VERSION);
|
||||
int tokenVersion = tokenVersionRaw != null ? tokenVersionRaw : 0;
|
||||
Object issuedAtObj = payloads.get(JWTPayload.ISSUED_AT);
|
||||
long issuedAtSeconds = 0;
|
||||
if (issuedAtObj instanceof Date issuedAtDate) {
|
||||
issuedAtSeconds = issuedAtDate.getTime() / 1000;
|
||||
}
|
||||
|
||||
String versionKey = StrUtil.format(RedisConstants.Auth.USER_SECURITY_VERSION, userId);
|
||||
Integer currentVersionRaw = (Integer) redisTemplate.opsForValue().get(versionKey);
|
||||
int currentVersion = currentVersionRaw != null ? currentVersionRaw : 0;
|
||||
String validAfterKey = StrUtil.format(RedisConstants.Auth.USER_TOKEN_VALID_AFTER, userId);
|
||||
Object validAfterObj = redisTemplate.opsForValue().get(validAfterKey);
|
||||
long validAfterSeconds = validAfterObj != null ? Convert.toLong(validAfterObj) : 0;
|
||||
|
||||
// 如果当前版本号比 Token 携带的版本号新,则认为该 Token 已失效
|
||||
if (tokenVersion < currentVersion) {
|
||||
if (issuedAtSeconds < validAfterSeconds) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 判断是否在黑名单中,如果在,则返回 false 标识Token无效
|
||||
// 3. 判断 Token 是否已被撤销(单端退出/会话注销)
|
||||
// 场景示例:单点退出登录、后台手动注销某个会话、封禁账号后立即阻断当前 Token 等
|
||||
if (Boolean.TRUE.equals(redisTemplate.hasKey(StrUtil.format(RedisConstants.Auth.BLACKLIST_TOKEN, jti)))) {
|
||||
if (isTokenRevoked(jti)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -190,29 +226,63 @@ public class JwtTokenManager implements TokenManager {
|
||||
}
|
||||
JWT jwt = JWTUtil.parseToken(token);
|
||||
JSONObject payloads = jwt.getPayloads();
|
||||
String jti = payloads.getStr(JWTPayload.JWT_ID);
|
||||
Integer expirationAt = payloads.getInt(JWTPayload.EXPIRES_AT);
|
||||
// 黑名单Token Key
|
||||
String blacklistTokenKey = StrUtil.format(RedisConstants.Auth.BLACKLIST_TOKEN, payloads.getStr(JWTPayload.JWT_ID));
|
||||
revokeTokenByJti(jti, expirationAt);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查Token是否已被撤销
|
||||
*
|
||||
* @param jti Token唯一标识
|
||||
* @return true-已撤销,false-未撤销
|
||||
*/
|
||||
private boolean isTokenRevoked(String jti) {
|
||||
if (StringUtils.isBlank(jti)) {
|
||||
return false;
|
||||
}
|
||||
return Boolean.TRUE.equals(redisTemplate.hasKey(StrUtil.format(RedisConstants.Auth.REVOKED_JTI, jti)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将Token加入撤销黑名单
|
||||
* <p>
|
||||
* 黑名单有效期与Token剩余有效期一致,避免永久存储
|
||||
*
|
||||
* @param jti Token唯一标识
|
||||
* @param expirationAt Token过期时间戳
|
||||
*/
|
||||
private void revokeTokenByJti(String jti, Integer expirationAt) {
|
||||
if (StringUtils.isBlank(jti)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String revokedJtiKey = StrUtil.format(RedisConstants.Auth.REVOKED_JTI, jti);
|
||||
if (expirationAt != null) {
|
||||
int currentTimeSeconds = Convert.toInt(System.currentTimeMillis() / 1000);
|
||||
if (expirationAt < currentTimeSeconds) {
|
||||
// Token已过期,直接返回
|
||||
return;
|
||||
}
|
||||
// 计算Token剩余时间,将其加入黑名单(值使用简单布尔标记即可)
|
||||
int expirationIn = expirationAt - currentTimeSeconds;
|
||||
redisTemplate.opsForValue().set(blacklistTokenKey, Boolean.TRUE, expirationIn, TimeUnit.SECONDS);
|
||||
redisTemplate.opsForValue().set(revokedJtiKey, Boolean.TRUE, expirationIn, TimeUnit.SECONDS);
|
||||
} else {
|
||||
// 永不过期的Token永久加入黑名单
|
||||
redisTemplate.opsForValue().set(blacklistTokenKey, Boolean.TRUE);
|
||||
redisTemplate.opsForValue().set(revokedJtiKey, Boolean.TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 失效指定用户的所有会话
|
||||
* <p>
|
||||
* 通过提升用户的安全版本号,使携带旧版本号的 Token 在后续校验时全部失效
|
||||
* 通过更新用户 tokenValidAfter 时间戳,使早于该时间签发的 Token 全部失效。
|
||||
* <p>
|
||||
* 适用场景:
|
||||
* <ul>
|
||||
* <li>用户修改密码</li>
|
||||
* <li>管理员强制下线用户</li>
|
||||
* <li>用户主动踢出所有设备</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param userId 用户ID
|
||||
*/
|
||||
@Override
|
||||
public void invalidateUserSessions(Long userId) {
|
||||
@@ -220,10 +290,10 @@ public class JwtTokenManager implements TokenManager {
|
||||
return;
|
||||
}
|
||||
|
||||
String versionKey = StrUtil.format(RedisConstants.Auth.USER_SECURITY_VERSION, userId);
|
||||
// 递增版本号
|
||||
redisTemplate.opsForValue().increment(versionKey);
|
||||
|
||||
String validAfterKey = StrUtil.format(RedisConstants.Auth.USER_TOKEN_VALID_AFTER, userId);
|
||||
long nowSeconds = System.currentTimeMillis() / 1000;
|
||||
// 设置过期时间,避免Redis内存泄漏
|
||||
redisTemplate.opsForValue().set(validAfterKey, nowSeconds, TOKEN_VALID_AFTER_TTL_SECONDS, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -253,8 +323,8 @@ public class JwtTokenManager implements TokenManager {
|
||||
* 生成 JWT Token
|
||||
*
|
||||
* @param authentication 认证信息
|
||||
* @param ttl 过期时间
|
||||
* @return JWT Token
|
||||
* @param ttl 过期时间(秒),-1表示永不过期
|
||||
* @return JWT Token字符串
|
||||
*/
|
||||
private String generateToken(Authentication authentication, int ttl) {
|
||||
return generateToken(authentication, ttl, false);
|
||||
@@ -263,18 +333,43 @@ public class JwtTokenManager implements TokenManager {
|
||||
|
||||
/**
|
||||
* 生成 JWT Token
|
||||
* <p>
|
||||
* Payload包含:
|
||||
* <ul>
|
||||
* <li>userId - 用户ID</li>
|
||||
* <li>deptId - 部门ID</li>
|
||||
* <li>dataScopes - 数据权限列表</li>
|
||||
* <li>authorities - 角色权限集合</li>
|
||||
* <li>tokenType - 是否为刷新令牌</li>
|
||||
* <li>iat/exp - 签发/过期时间</li>
|
||||
* <li>jti - Token唯一标识(用于撤销)</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param authentication 认证信息
|
||||
* @param ttl 过期时间
|
||||
* @param isRefreshToken 类型是否为刷新token
|
||||
* @return JWT Token
|
||||
* @param authentication 认证信息
|
||||
* @param ttl 过期时间(秒)
|
||||
* @param isRefreshToken 是否为刷新令牌
|
||||
* @return JWT Token字符串
|
||||
*/
|
||||
private String generateToken(Authentication authentication, int ttl, boolean isRefreshToken) {
|
||||
SysUserDetails userDetails = (SysUserDetails) authentication.getPrincipal();
|
||||
Map<String, Object> payload = new HashMap<>();
|
||||
payload.put(JwtClaimConstants.USER_ID, userDetails.getUserId()); // 用户ID
|
||||
payload.put(JwtClaimConstants.DEPT_ID, userDetails.getDeptId()); // 部门ID
|
||||
payload.put(JwtClaimConstants.DATA_SCOPE, userDetails.getDataScope()); // 数据权限范围
|
||||
|
||||
// 存储数据权限列表
|
||||
List<RoleDataScope> dataScopes = userDetails.getDataScopes();
|
||||
if (dataScopes != null && !dataScopes.isEmpty()) {
|
||||
List<Map<String, Object>> scopesList = dataScopes.stream()
|
||||
.map(scope -> {
|
||||
Map<String, Object> scopeMap = new HashMap<>();
|
||||
scopeMap.put("roleCode", scope.getRoleCode());
|
||||
scopeMap.put("dataScope", scope.getDataScope());
|
||||
scopeMap.put("customDeptIds", scope.getCustomDeptIds());
|
||||
return scopeMap;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
payload.put(JwtClaimConstants.DATA_SCOPES, scopesList);
|
||||
}
|
||||
|
||||
// claims 中添加角色信息
|
||||
Set<String> roles = authentication.getAuthorities().stream()
|
||||
@@ -289,12 +384,6 @@ public class JwtTokenManager implements TokenManager {
|
||||
payload.put(JwtClaimConstants.TOKEN_TYPE, true);
|
||||
}
|
||||
|
||||
// 设置安全版本号:不存在时默认为 0
|
||||
String versionKey = StrUtil.format(RedisConstants.Auth.USER_SECURITY_VERSION, userDetails.getUserId());
|
||||
Integer currentVersion = (Integer) redisTemplate.opsForValue().get(versionKey);
|
||||
int securityVersion = currentVersion != null ? currentVersion : 0;
|
||||
payload.put(JwtClaimConstants.SECURITY_VERSION, securityVersion);
|
||||
|
||||
// 设置过期时间 -1 表示永不过期
|
||||
if (ttl != -1) {
|
||||
Date expiresAt = DateUtil.offsetSecond(now, ttl);
|
||||
|
||||
@@ -8,7 +8,8 @@ import com.youlai.boot.core.exception.BusinessException;
|
||||
import com.youlai.boot.core.web.ResultCode;
|
||||
import com.youlai.boot.config.property.SecurityProperties;
|
||||
import com.youlai.boot.security.model.AuthenticationToken;
|
||||
import com.youlai.boot.security.model.OnlineUser;
|
||||
import com.youlai.boot.security.model.UserSession;
|
||||
import com.youlai.boot.security.model.RoleDataScope;
|
||||
import com.youlai.boot.security.model.SysUserDetails;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
@@ -26,7 +27,15 @@ import java.util.stream.Collectors;
|
||||
/**
|
||||
* Redis Token 管理器
|
||||
* <p>
|
||||
* 用于生成、解析、校验、刷新 Redis Token
|
||||
* 实现基于Redis的有状态认证,支持:
|
||||
* <ul>
|
||||
* <li>Access Token + Refresh Token 双令牌机制</li>
|
||||
* <li>单设备/多设备登录控制</li>
|
||||
* <li>用户级会话失效</li>
|
||||
* <li>在线用户管理</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* 与JWT模式相比,Redis模式支持主动踢人、在线用户查询等功能
|
||||
*
|
||||
* @author Ray.Hao
|
||||
* @since 2024/11/15
|
||||
@@ -55,19 +64,19 @@ public class RedisTokenManager implements TokenManager {
|
||||
String accessToken = IdUtil.fastSimpleUUID();
|
||||
String refreshToken = IdUtil.fastSimpleUUID();
|
||||
|
||||
// 构建用户在线信息
|
||||
OnlineUser onlineUser = new OnlineUser(
|
||||
// 构建用户会话信息
|
||||
UserSession userSession = new UserSession(
|
||||
user.getUserId(),
|
||||
user.getUsername(),
|
||||
user.getDeptId(),
|
||||
user.getDataScope(),
|
||||
user.getDataScopes(),
|
||||
user.getAuthorities().stream()
|
||||
.map(GrantedAuthority::getAuthority)
|
||||
.collect(Collectors.toSet())
|
||||
);
|
||||
|
||||
// 存储访问令牌、刷新令牌和刷新令牌映射
|
||||
storeTokensInRedis(accessToken, refreshToken, onlineUser);
|
||||
storeTokensInRedis(accessToken, refreshToken, userSession);
|
||||
|
||||
// 单设备登录控制
|
||||
handleSingleDeviceLogin(user.getUserId(), accessToken);
|
||||
@@ -87,13 +96,13 @@ public class RedisTokenManager implements TokenManager {
|
||||
*/
|
||||
@Override
|
||||
public Authentication parseToken(String token) {
|
||||
OnlineUser onlineUser = (OnlineUser) redisTemplate.opsForValue().get(formatTokenKey(token));
|
||||
if (onlineUser == null) return null;
|
||||
UserSession userSession = (UserSession) redisTemplate.opsForValue().get(formatTokenKey(token));
|
||||
if (userSession == null) return null;
|
||||
|
||||
// 构建用户权限集合
|
||||
Set<SimpleGrantedAuthority> authorities = null;
|
||||
|
||||
Set<String> roles = onlineUser.getRoles();
|
||||
Set<String> roles = userSession.getRoles();
|
||||
if (CollectionUtil.isNotEmpty(roles)) {
|
||||
authorities = roles.stream()
|
||||
.map(SimpleGrantedAuthority::new)
|
||||
@@ -101,7 +110,7 @@ public class RedisTokenManager implements TokenManager {
|
||||
}
|
||||
|
||||
// 构建用户详情对象
|
||||
SysUserDetails userDetails = buildUserDetails(onlineUser, authorities);
|
||||
SysUserDetails userDetails = buildUserDetails(userSession, authorities);
|
||||
return new UsernamePasswordAuthenticationToken(userDetails, null, authorities);
|
||||
}
|
||||
|
||||
@@ -135,12 +144,12 @@ public class RedisTokenManager implements TokenManager {
|
||||
*/
|
||||
@Override
|
||||
public AuthenticationToken refreshToken(String refreshToken) {
|
||||
OnlineUser onlineUser = (OnlineUser) redisTemplate.opsForValue()
|
||||
UserSession userSession = (UserSession) redisTemplate.opsForValue()
|
||||
.get(StrUtil.format(RedisConstants.Auth.REFRESH_TOKEN_USER, refreshToken));
|
||||
if (onlineUser == null) {
|
||||
if (userSession == null) {
|
||||
throw new BusinessException(ResultCode.REFRESH_TOKEN_INVALID);
|
||||
}
|
||||
Object oldAccessTokenValue = redisTemplate.opsForValue().get(StrUtil.format(RedisConstants.Auth.USER_ACCESS_TOKEN, onlineUser.getUserId()));
|
||||
Object oldAccessTokenValue = redisTemplate.opsForValue().get(StrUtil.format(RedisConstants.Auth.USER_ACCESS_TOKEN, userSession.getUserId()));
|
||||
// 删除旧的访问令牌记录
|
||||
Optional.of(oldAccessTokenValue)
|
||||
.map(String.class::cast)
|
||||
@@ -148,7 +157,7 @@ public class RedisTokenManager implements TokenManager {
|
||||
|
||||
// 生成新访问令牌并存储
|
||||
String newAccessToken = IdUtil.fastSimpleUUID();
|
||||
storeAccessToken(newAccessToken, onlineUser);
|
||||
storeAccessToken(newAccessToken, userSession);
|
||||
|
||||
int accessTtl = securityProperties.getSession().getAccessTokenTimeToLive();
|
||||
return AuthenticationToken.builder()
|
||||
@@ -166,14 +175,16 @@ public class RedisTokenManager implements TokenManager {
|
||||
@Override
|
||||
public void invalidateToken(String token) {
|
||||
Object value = redisTemplate.opsForValue().get(formatTokenKey(token));
|
||||
if (value instanceof OnlineUser onlineUser) {
|
||||
Long userId = onlineUser.getUserId();
|
||||
if (value instanceof UserSession userSession) {
|
||||
Long userId = userSession.getUserId();
|
||||
invalidateUserSessions(userId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使指定用户的所有会话失效
|
||||
* <p>
|
||||
* 适用场景:用户修改密码、管理员强制下线、账号封禁等
|
||||
*
|
||||
* @param userId 用户ID
|
||||
*/
|
||||
@@ -207,24 +218,26 @@ public class RedisTokenManager implements TokenManager {
|
||||
*
|
||||
* @param accessToken 访问令牌
|
||||
* @param refreshToken 刷新令牌
|
||||
* @param onlineUser 在线用户信息
|
||||
* @param userSession 用户会话信息
|
||||
*/
|
||||
private void storeTokensInRedis(String accessToken, String refreshToken, OnlineUser onlineUser) {
|
||||
private void storeTokensInRedis(String accessToken, String refreshToken, UserSession userSession) {
|
||||
// 访问令牌 -> 用户信息
|
||||
setRedisValue(formatTokenKey(accessToken), onlineUser, securityProperties.getSession().getAccessTokenTimeToLive());
|
||||
setRedisValue(formatTokenKey(accessToken), userSession, securityProperties.getSession().getAccessTokenTimeToLive());
|
||||
|
||||
// 刷新令牌 -> 用户信息
|
||||
String refreshTokenKey = StrUtil.format(RedisConstants.Auth.REFRESH_TOKEN_USER, refreshToken);
|
||||
setRedisValue(refreshTokenKey, onlineUser, securityProperties.getSession().getRefreshTokenTimeToLive());
|
||||
setRedisValue(refreshTokenKey, userSession, securityProperties.getSession().getRefreshTokenTimeToLive());
|
||||
|
||||
// 用户ID -> 刷新令牌
|
||||
setRedisValue(StrUtil.format(RedisConstants.Auth.USER_REFRESH_TOKEN, onlineUser.getUserId()),
|
||||
setRedisValue(StrUtil.format(RedisConstants.Auth.USER_REFRESH_TOKEN, userSession.getUserId()),
|
||||
refreshToken,
|
||||
securityProperties.getSession().getRefreshTokenTimeToLive());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理单设备登录控制
|
||||
* <p>
|
||||
* 当配置不允许多设备登录时,新登录会使旧Token失效
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param accessToken 新生成的访问令牌
|
||||
@@ -247,27 +260,27 @@ public class RedisTokenManager implements TokenManager {
|
||||
* 存储新的访问令牌
|
||||
*
|
||||
* @param newAccessToken 新访问令牌
|
||||
* @param onlineUser 在线用户信息
|
||||
* @param userSession 用户会话信息
|
||||
*/
|
||||
private void storeAccessToken(String newAccessToken, OnlineUser onlineUser) {
|
||||
setRedisValue(StrUtil.format(RedisConstants.Auth.ACCESS_TOKEN_USER, newAccessToken), onlineUser, securityProperties.getSession().getAccessTokenTimeToLive());
|
||||
String userAccessKey = StrUtil.format(RedisConstants.Auth.USER_ACCESS_TOKEN, onlineUser.getUserId());
|
||||
private void storeAccessToken(String newAccessToken, UserSession userSession) {
|
||||
setRedisValue(StrUtil.format(RedisConstants.Auth.ACCESS_TOKEN_USER, newAccessToken), userSession, securityProperties.getSession().getAccessTokenTimeToLive());
|
||||
String userAccessKey = StrUtil.format(RedisConstants.Auth.USER_ACCESS_TOKEN, userSession.getUserId());
|
||||
setRedisValue(userAccessKey, newAccessToken, securityProperties.getSession().getAccessTokenTimeToLive());
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建用户详情对象
|
||||
*
|
||||
* @param onlineUser 在线用户信息
|
||||
* @param userSession 用户会话信息
|
||||
* @param authorities 权限集合
|
||||
* @return SysUserDetails 用户详情
|
||||
*/
|
||||
private SysUserDetails buildUserDetails(OnlineUser onlineUser, Set<SimpleGrantedAuthority> authorities) {
|
||||
private SysUserDetails buildUserDetails(UserSession userSession, Set<SimpleGrantedAuthority> authorities) {
|
||||
SysUserDetails userDetails = new SysUserDetails();
|
||||
userDetails.setUserId(onlineUser.getUserId());
|
||||
userDetails.setUsername(onlineUser.getUsername());
|
||||
userDetails.setDeptId(onlineUser.getDeptId());
|
||||
userDetails.setDataScope(onlineUser.getDataScope());
|
||||
userDetails.setUserId(userSession.getUserId());
|
||||
userDetails.setUsername(userSession.getUsername());
|
||||
userDetails.setDeptId(userSession.getDeptId());
|
||||
userDetails.setDataScopes(userSession.getDataScopes());
|
||||
userDetails.setAuthorities(authorities);
|
||||
return userDetails;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user