项目共用redis
-
-
- 一、登录流程(v1和v2都是一样的)
- 二、在保存redis用户信息里面,给用户加一个标识
- 三、TokenService
- [四、JwtAuthenticationTokenFilter token过滤器](#四、JwtAuthenticationTokenFilter token过滤器)
-
问题描述
java
当前项目是v2版本,v1版本和这代码用户登录和获取token验证一样逻辑,一样都是springsecurity,redis一致。jwt也一致导致同一个token可以两个项目都能使用。但是v1和v2共用一个用户中心服务,但是他们角色roles,设备deviceIds,permissions不一致,导致v1登录后redis,切换v2项目不用重新登录,v2根据请求token最后获取到v1项目的redis缓存信息
{"@type":"com.skms.common.core.domain.model.LoginUser","browser":"Chrome 14","expireTime":1778550957853,"ipaddr":"10.168.88.249","loginLocation":"内网IP","loginTime":1778549157853,"os":"Windows 10","permissions":Set["system:user:resetPwd","base:ukey:delFile","monitor:logininfor:remove","system:user:query","system:use:remove","base:keyHistory:list","system:user:export","secure:cert:list","secure:cert:request","system:page:query","system:role:remove","skms:log:remove","base:key:remove","base:keyDistribute:add","base:keyDesc:remove","fisherman:card:backup","base:symmetricKey:importKeyPart","base:node:add","base:keyHistory:remove","base:nodeCreate:remove","base:node:import","monitor:logininfor:list","base:symmetricKey:list","base:keyDesc:edit","base:node:list","skms:ukey:edit","secure:cert:remove","base:key:export","kms:online:revoke","system:create:list","base:keyDistribute:keyGraph","system:create:add","system:user:edit","secure:cardUser:delete","system:role:edit","base:ukey:listFile","skms:ukey:list","monitor:operlog:remove","base:symmetricKey:add","skms:ukey:export","system:role:add","system:user:bindUKey","skms:ukey:remove","kms:offlinekey:recovery","monitor:logininfor:query","secure:cert:caCert","base:key:add","base:node:remove","base:keyHistory:add","skms:log:query","base:symmetricKey:importUkey","base:nodeCreate:query","base:keyDesc:add","system:role:export","skms:keys:generate","base:keyHistory:query","secure:cert:export","system:create:query","base:page:edit","kms:online:publish","base:nodeCreate:edit","monitor:operlog:export","kms:online:enable","base:symmetricKey:updateStatus","system:use:query","base:keyHistory:edit","system:user:add","base:keyDistribute:edit","monitor:operlog:add","base:nodeCreate:export","base:nodeCreate:list","skms:log:export","kms:offlinekey:export","base:keyDistribute:query","base:keyDistribute:remove","base:ukey:readFile","base:nodeCreate:add","base:node:query","system:use:add","base:symmetricKey:exportKeyPart","system:create:remove","base:symmetricKey:edit","base:key:edit","kms:online:backup","secure:cardUser:edit","monitor:operlog:edit","fisherman:card:changePin","base:symmetricKey:distributeKey","system:user:remove","base:symmetricKey:query","system:role:list","system:user:import","secure:cardUser:add","base:keyDistribute:export","base:keyHistory:export","skms:ukey:add","kms:online:recovery","monitor:operlog:list","system:use:edit","base:symmetricKey:exportUkey","monitor:logininfor:export","system:use:export","monitor:operlog:query","skms:log:list","system:user:list","base:symmetricKey:reconfig","fisherman:card:login","base:symmetricKey:remove","kms:online:judiciary","secure:cardUser:info","secure:cert:signCert","base:node:edit","skms:ukey:query","fisherman:card:recovery","system:role:query","fisherman:card:logout","base:symmetricKey:export"],"token":"a798cb45-e7ce-409b-ae0b-ac3c92684d61","user":{"@type":"com.skms.common.core.domain.entity.FeignSysUser","admin":false,"avatar":"1","certId":72,"createBy":"admin","createTime":1755738648000,"delFlag":"0","deviceIds":[],"email":"","keyCode":"GKLZ022508023","loginDate":1778492175000,"loginIp":"10.168.88.249","nickName":"xtgly","params":{"@type":"java.util.LinkedHashMap"},"password":"$2a$10$qFyCTChYPzfxVr.kDym0NOT4b/evfbFQw908RSFDEaOu/QkCDwCT2","phonenumber":"","roleIds":[2L,7L,8L,9L],"roles":[{"admin":false,"dataScope":"1","deptCheckStrictly":false,"flag":false,"menuCheckStrictly":false,"params":{"@type":"java.util.HashMap"},"permissions":Set["monitor:operlog:export","monitor:operlog:query","skms:log:list","skms:log:query","monitor:logininfor:remove","monitor:operlog:add","monitor:logininfor:list","monitor:operlog:remove","monitor:operlog:list","skms:log:remove","skms:log:export","monitor:logininfor:query","monitor:logininfor:export","monitor:operlog:edit"],"roleId":2,"roleKey":"auditManager","roleName":"审计管理员","roleSort":"3"},{"admin":false,"dataScope":"1","deptCheckStrictly":false,"flag":false,"menuCheckStrictly":false,"params":{"@type":"java.util.HashMap"},"permissions":Set["system:user:resetPwd","fisherman:card:changePin","system:user:remove","secure:cert:remove","system:role:list","system:user:import","secure:cardUser:add","system:user:edit","secure:cardUser:delete","system:user:query","system:role:edit","system:user:add","system:user:export","secure:cert:list","secure:cert:request","system:role:remove","system:role:add","system:user:bindUKey","secure:cert:caCert","fisherman:card:backup","system:user:list","fisherman:card:login","system:role:export","secure:cardUser:info","secure:cert:signCert","secure:cert:export","secure:cardUser:edit","fisherman:card:recovery","system:role:query","fisherman:card:logout"],"roleId":7,"roleKey":"systemManager","roleName":"系统管理员","roleSort":"2"},{"admin":false,"dataScope":"1","deptCheckStrictly":false,"flag":false,"menuCheckStrictly":false,"params":{"@type":"java.util.HashMap"},"permissions":Set["base:ukey:delFile","kms:online:enable","base:symmetricKey:updateStatus","system:use:query","system:use:remove","base:keyHistory:edit","base:keyDistribute:edit","base:keyHistory:list","system:page:query","base:nodeCreate:export","base:nodeCreate:list","base:key:remove","base:keyDistribute:add","kms:offlinekey:export","base:keyDesc:remove","base:keyDistribute:query","base:keyDistribute:remove","base:ukey:readFile","base:symmetricKey:importKeyPart","base:node:add","base:nodeCreate:add","base:keyHistory:remove","base:node:query","base:nodeCreate:remove","system:use:add","base:symmetricKey:exportKeyPart","system:create:remove","base:symmetricKey:edit","base:node:import","base:key:edit","kms:online:backup","base:symmetricKey:distributeKey","base:symmetricKey:list","base:keyDesc:edit","base:node:list","skms:ukey:edit","base:key:export","base:symmetricKey:query","kms:online:revoke","system:create:list","base:keyDistribute:keyGraph","system:create:add","base:keyDistribute:export","base:ukey:listFile","base:keyHistory:export","skms:ukey:add","skms:ukey:list","kms:online:recovery","base:symmetricKey:add","skms:ukey:export","skms:ukey:remove","system:use:edit","base:symmetricKey:exportUkey","base:key:add","system:use:export","base:node:remove","base:keyHistory:add","base:symmetricKey:reconfig","base:symmetricKey:remove","base:symmetricKey:importUkey","base:nodeCreate:query","base:keyDesc:add","base:node:edit","skms:keys:generate","skms:ukey:query","base:keyHistory:query","system:create:query","base:page:edit","kms:online:publish","base:nodeCreate:edit","base:symmetricKey:export"],"roleId":8,"roleKey":"businessManager","roleName":"业务管理员","roleSort":"4"},{"admin":false,"dataScope":"1","deptCheckStrictly":false,"flag":false,"menuCheckStrictly":false,"params":{"@type":"java.util.HashMap"},"permissions":Set["kms:offlinekey:recovery","kms:online:judiciary"],"roleId":9,"roleKey":"judicatureManager","roleName":"司法恢复管理员","roleSort":"5"}],"sex":"0","status":"0","updateBy":"nothing","updateTime":1778463375000,"userId":38,"userName":"xtgly"},"userId":38,"username":"xtgly"}
导致PermissionService里面hasPermi 报错,明明用户在v2关联的权限,但是实际还是获取到v1的权限,导致的。
有啥办法在缓存加个标识(或者在保存redis的数据里加一个标识,获取发现不是当前版本,重新往redis赋值当前版本并更新springSecurity上下文)
ai回答:
这是一个典型的多版本应用共享 Redis 缓存导致的数据不一致问题。v1 和 v2 共用同一个用户中心服务和相同的 JWT token,但权限体系不同,导致 v2 请求时会获取到 v1 缓存的权限信息。
java
v1 登录: v1 登录后 Redis 缓存的 LoginUser.appVersion = "v1"
v2 访问: v2 使用 v1 的 token 访问
JwtAuthenticationTokenFilter.getLoginUser() 从 Redis 获取 LoginUser
检查 appVersion ("v1") != 当前版本 ("v2")
返回 null 触发重新获取
重新获取: 通过 UserClientApi 从用户中心获取最新用户信息
刷新缓存: 使用 v2 的权限重新构建 LoginUser,设置 appVersion = "v2",刷新 Redis
设置 SecurityContext: 将新的 LoginUser 设置到 SecurityContext
一、登录流程(v1和v2都是一样的)
参考之前blog 用户登录:温故一下(验证码和jwt简单创建和验证)
(一定要看,重点看UserDetailsServiceImpl , TokenService, JwtAuthenticationTokenFilter )
大概描述一下:
登录:
1.登录验证
2.生成token并保存用户信息到redis
java
// 该方法会去调用UserDetailsServiceImpl.loadUserByUsername(登录验证)
Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
// 生成token并保存用户信息到redis
return tokenService.createToken(loginUser);
和blog 用户登录:温故一下(验证码和jwt简单创建和验证)里UserDetailsService相比:
loadUserByUsername 原来是从本地获取用户信息和权限,菜单等一系列数据,现在这个远程获取用户信息,但是权限,菜单等还要用本地的(当前版本)
java
/**
* 用户验证处理
*
* @author skms
*/
@Service("UserDetailsServiceImpl")
public class UserDetailsServiceImpl implements UserDetailsService {
private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class);
@Resource
private ISysUserService userService;
@Resource
private ISysUserDeviceService sysUserDeviceService;
@Resource
private SysPermissionService permissionService;
@Resource
private UserClientApi userClientApi;
@Value("${kms.userCenter}")
private String userCenter;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// SysUser user = userService.selectUserByUserName(username);
log.info("配置的 userCenter: {}", userCenter);
FeignSysUser internalInfoByUserName = userClientApi.getInternalInfoByUserName(username);
// 补充角色信息:根据远程返回的用户 ID 查询本地角色表获取 roles , roleIds ,deviceIds
if (internalInfoByUserName != null && internalInfoByUserName.getUserId() != null) {
// 查询角色 ID 列表
List<Long> roleIds = userService.selectRoleIdsByUserId(internalInfoByUserName.getUserId());
internalInfoByUserName.setRoleIds(roleIds.toArray(new Long[0]));
// 查询角色对象列表
List<SysRole> roles = userService.selectRolesByUserId(internalInfoByUserName.getUserId());
internalInfoByUserName.setRoles(roles);
List<String> deviceIds = sysUserDeviceService.selectSysDeviceIdsByUserId(internalInfoByUserName.getUserId());
internalInfoByUserName.setDeviceIds(deviceIds);
}
SysUser user = FeignTransferUser.toFeignSysUser(internalInfoByUserName);
if (StringUtils.isNull(user)) {
log.info("登录用户:{} 不存在.", username);
throw new RuntimeException("登录用户:" + username + " 不存在");
} else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) {
log.info("登录用户:{} 已被删除.", username);
throw new RuntimeException("对不起,您的账号:" + username + " 已被删除");
} else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
log.info("登录用户:{} 已被停用.", username);
throw new RuntimeException("对不起,您的账号:" + username + " 已停用");
}
return createLoginUser(user);
}
public UserDetails createLoginUser(SysUser user) {
return new LoginUser(user.getUserId(), user, permissionService.getMenuPermission(user));
}
}
在blog 用户登录:温故一下(验证码和jwt简单创建和验证) 的
TokenService 可以看到
createToken 先保存缓存,调用 refreshToken 保存到redis
JwtAuthenticationTokenFilter 可以看到从redis去用户信息去设置springSecurity的上下文
二、在保存redis用户信息里面,给用户加一个标识
配置文件(v1和v2 jwt 令牌一致,每个项目各自加各自的appVersion 标识)
yaml
# token配置
token:
# 令牌自定义标识
header: Authorization
# 令牌密钥
secret: abcdefghijklmnopqrstuvwxyz
# 令牌有效期(默认30分钟)
expireTime: 30
appVersion: v2 # 新增:应用版本标识
实体类UserDetails ,和blog 用户登录:温故一下(验证码和jwt简单创建和验证)里面实体类相比:多一个字段 appVersion
java
/**
* 登录用户身份权限
*
* @author skms
*/
public class LoginUser implements UserDetails {
private static final long serialVersionUID = 1L;
/**
* 用户ID
*/
private Long userId;
/**
* 用户唯一标识
*/
private String token;
/**
* 登录时间
*/
private Long loginTime;
/**
* 过期时间
*/
private Long expireTime;
/**
* 登录IP地址
*/
private String ipaddr;
/**
* 登录地点
*/
private String loginLocation;
/**
* 浏览器类型
*/
private String browser;
/**
* 操作系统
*/
private String os;
/**
* 权限列表
*/
private Set<String> permissions;
/**
* 用户信息
*/
private SysUser user;
/**
* 应用版本标识
*/
private String appVersion;
public LoginUser() {
}
public LoginUser(SysUser user, Set<String> permissions) {
this.user = user;
this.permissions = permissions;
}
public LoginUser(Long userId, SysUser user, Set<String> permissions) {
this.userId = userId;
this.user = user;
this.permissions = permissions;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
@JSONField(serialize = false)
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUserName();
}
/**
* 账户是否未过期,过期无法验证
*/
@JSONField(serialize = false)
@Override
public boolean isAccountNonExpired() {
return true;
}
/**
* 指定用户是否解锁,锁定的用户无法进行身份验证
*
* @return
*/
@JSONField(serialize = false)
@Override
public boolean isAccountNonLocked() {
return true;
}
/**
* 指示是否已过期的用户的凭据(密码),过期的凭据防止认证
*
* @return 结果
*/
@JSONField(serialize = false)
@Override
public boolean isCredentialsNonExpired() {
return true;
}
/**
* 是否可用 ,禁用的用户不能身份验证
*
* @return 结果
*/
@JSONField(serialize = false)
@Override
public boolean isEnabled() {
return true;
}
public Long getLoginTime() {
return loginTime;
}
public void setLoginTime(Long loginTime) {
this.loginTime = loginTime;
}
public String getIpaddr() {
return ipaddr;
}
public void setIpaddr(String ipaddr) {
this.ipaddr = ipaddr;
}
public String getLoginLocation() {
return loginLocation;
}
public void setLoginLocation(String loginLocation) {
this.loginLocation = loginLocation;
}
public String getBrowser() {
return browser;
}
public void setBrowser(String browser) {
this.browser = browser;
}
public String getOs() {
return os;
}
public void setOs(String os) {
this.os = os;
}
public Long getExpireTime() {
return expireTime;
}
public void setExpireTime(Long expireTime) {
this.expireTime = expireTime;
}
public Set<String> getPermissions() {
return permissions;
}
public void setPermissions(Set<String> permissions) {
this.permissions = permissions;
}
public SysUser getUser() {
return user;
}
public void setUser(SysUser user) {
this.user = user;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
public String getAppVersion() {
return appVersion;
}
public void setAppVersion(String appVersion) {
this.appVersion = appVersion;
}
}
三、TokenService
和blog 用户登录:温故一下(验证码和jwt简单创建和验证)里面TokenService 相比
1.获取用户身份信息 判断是否是当前版本
2.增加refreshUserFromUserCenter:从用户中心重新获取用户信息(适用于版本不匹配场景)
3.refreshToken:redis保存时候增加版本标识
java
/**
* token 验证处理
*
* @author skms
*/
@Slf4j
@Component
public class TokenService {
protected static final long MILLIS_SECOND = 1000;
protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;
private static final Long MILLIS_MINUTE_TEN = 20 * 60 * 1000L;
/**
* 令牌自定义标识
*/
@Value("${token.header}")
private String header;
/**
* 令牌秘钥
*/
@Value("${token.secret}")
private String secret;
/**
* 令牌有效期(默认 30 分钟)
*/
@Value("${token.expireTime}")
private int expireTime;
/**
* 应用版本标识
*/
@Value("${token.appVersion:v2}")
private String appVersion;
@Resource
private RedisCache redisCache;
@Resource
private ISysUserService userService;
@Resource
private UserClientApi userClientApi;
@Resource
private SysPermissionService permissionService;
@Resource
private ISysUserDeviceService sysUserDeviceService;
@Resource
private ICertService certService;
@Resource
@Qualifier("UserDetailsServiceImpl")
private UserDetailsService userDetailsService;
/**
* 获取用户身份信息
*
* @return 用户信息
*/
public LoginUser getLoginUser(HttpServletRequest request) {
// 获取请求携带的令牌
String token = getToken(request);
if (StringUtils.isNotEmpty(token)) {
try {
Claims claims = parseToken(token);
// 解析对应的权限以及用户信息
String uuid = (String) claims.get(Constants.LOGIN_USER_KEY);
String userKey = getTokenKey(uuid);
LoginUser loginUser = redisCache.getCacheObject(userKey);
// 验证版本是否匹配
if (loginUser != null && !isCurrentVersion(loginUser)) {
// 版本不匹配,返回 null 触发重新获取
return null;
}
return loginUser;
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
return null;
}
/**
* 设置用户身份信息
*/
public void setLoginUser(LoginUser loginUser) {
if (StringUtils.isNotNull(loginUser) && StringUtils.isNotEmpty(loginUser.getToken())) {
refreshToken(loginUser);
}
}
/**
* 删除用户身份信息
*/
public void delLoginUser(String token) {
if (StringUtils.isNotEmpty(token)) {
String userKey = getTokenKey(token);
redisCache.deleteObject(userKey);
}
}
/**
* 创建令牌
*
* @param loginUser 用户信息
* @return 令牌
*/
public String createToken(LoginUser loginUser) {
String token = IdUtils.fastUUID();
loginUser.setToken(token);
setUserAgent(loginUser);
refreshToken(loginUser);
Map<String, Object> claims = new HashMap<>(16);
claims.put(Constants.LOGIN_USER_KEY, token);
return createToken(claims);
}
/**
* 验证令牌是否有效(未过期)
*
* @param loginUser 登录用户
* @return 是否有效
*/
public boolean isTokenValid(LoginUser loginUser) {
long expireTime = loginUser.getExpireTime();
long currentTime = System.currentTimeMillis();
// Token 未过期且有效期不足 20 分钟时才认为是有效的
return expireTime - currentTime > MILLIS_MINUTE_TEN;
}
/**
* 验证令牌有效期,相差不足 20 分钟,自动刷新缓存
*
* @param loginUser 登录用户
*/
public void verifyToken(LoginUser loginUser) {
long expireTime = loginUser.getExpireTime();
long currentTime = System.currentTimeMillis();
if (expireTime - currentTime <= MILLIS_MINUTE_TEN) {
refreshToken(loginUser);
}
}
/**
* 刷新令牌有效期
*
* @param loginUser 登录信息
*/
public void refreshToken(LoginUser loginUser) {
loginUser.setLoginTime(System.currentTimeMillis());
loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE);
// 设置应用版本标识
loginUser.setAppVersion(appVersion);
// 根据 uuid 将 loginUser 缓存
String userKey = getTokenKey(loginUser.getToken());
redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);
}
/**
* 验证用户缓存是否为当前应用版本
*/
private boolean isCurrentVersion(LoginUser loginUser) {
String currentVersion = appVersion != null ? appVersion : Constants.APP_VERSION_V2;
return currentVersion.equals(loginUser.getAppVersion());
}
/**
* 设置用户代理信息
*
* @param loginUser 登录信息
*/
public void setUserAgent(LoginUser loginUser) {
UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
loginUser.setIpaddr(ip);
loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
loginUser.setBrowser(userAgent.getBrowser().getName());
loginUser.setOs(userAgent.getOperatingSystem().getName());
}
/**
* 从数据声明生成令牌
*
* @param claims 数据声明
* @return 令牌
*/
private String createToken(Map<String, Object> claims) {
return Jwts.builder()
.setClaims(claims)
.signWith(SignatureAlgorithm.HS512, secret).compact();
}
/**
* 从令牌中获取数据声明
*
* @param token 令牌
* @return 数据声明
*/
private Claims parseToken(String token) {
return Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
}
/**
* 从令牌中获取用户名
*
* @param token 令牌
* @return 用户名
*/
public String getUsernameFromToken(String token) {
Claims claims = parseToken(token);
return claims.getSubject();
}
/**
* 获取请求 token
*
* @param request 请求
* @return token
*/
private String getToken(HttpServletRequest request) {
String token = request.getHeader(header);
if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) {
token = token.replace(Constants.TOKEN_PREFIX, "");
}
return token;
}
private String getTokenKey(String uuid) {
return Constants.LOGIN_TOKEN_KEY + uuid;
}
/**
* 从用户中心重新获取用户信息(适用于版本不匹配场景)
*/
public LoginUser refreshUserFromUserCenter(HttpServletRequest request) {
String token = getToken(request);
if (StringUtils.isEmpty(token)) {
return null;
}
try {
Claims claims = parseToken(token);
Object userIdObj = claims.get(Constants.JWT_USERID);
Long userId;
if (userIdObj instanceof Integer) {
userId = ((Integer) userIdObj).longValue();
} else if (userIdObj instanceof Long) {
userId = (Long) userIdObj;
} else {
log.error("重新获取用户信息:userId 类型错误,类型为{}", userIdObj == null ? "null" : userIdObj.getClass().getName());
return null;
}
if (userId == null) {
log.error("重新获取用户信息:userId 不存在.");
return null;
}
// 重新加载本地角色和设备信息(使用 v2 的数据)
List<Long> roleIds = userService.selectRoleIdsByUserId(feignSysUser.getUserId());
feignSysUser.setRoleIds(roleIds.toArray(new Long[0]));
List<SysRole> roles = userService.selectRolesByUserId(feignSysUser.getUserId());
feignSysUser.setRoles(roles);
List<String> deviceIds = sysUserDeviceService.selectSysDeviceIdsByUserId(feignSysUser.getUserId());
feignSysUser.setDeviceIds(deviceIds);
// 构建新的 LoginUser(使用 v2 的权限)
SysUser user = FeignTransferUser.toFeignSysUser(feignSysUser);
if (StringUtils.isNull(user)) {
log.error("重新获取用户信息:{} 不存在.", uuid);
throw new UsernameNotFoundException("重新获取用户信息:" + uuid + " 不存在");
} else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) {
log.error("重新获取用户信息:{} 已被删除.", uuid);
throw new BaseException("对不起,您的账号:" + uuid + " 已被删除");
} else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
log.error("重新获取用户信息:{} 已被停用.", uuid);
throw new BaseException("对不起,您的账号:" + uuid + " 已停用");
}
LoginUser loginUser = new LoginUser(feignSysUser.getUserId(), user, permissionService.getMenuPermission(user));
loginUser.setToken(token);
// 刷新 Redis 缓存(带新版本号)
refreshToken(loginUser);
return loginUser;
} catch (Exception e) {
log.error("重新获取用户信息失败", e);
return null;
}
}
}
四、JwtAuthenticationTokenFilter token过滤器
和blog 用户登录:温故一下(验证码和jwt简单创建和验证)里面JwtAuthenticationTokenFilter相比
1.重新获取用户信息并设置给springSecurity的上下文
java
/**
* token 过滤器 验证 token 有效性
*
* @author skms
*/
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
@Resource
private TokenService tokenService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
LoginUser loginUser = tokenService.getLoginUser(request);
// 情况 1: 版本不匹配或缓存不存在,loginUser 为 null
// 情况 2: SecurityContext 中没有认证信息
if (StringUtils.isNull(SecurityUtils.getAuthentication())) {
if (loginUser == null) {
// 版本不匹配或缓存不存在,通过用户中心重新获取权限并刷新缓存
loginUser = tokenService.refreshUserFromUserCenter(request);
} else if (!tokenService.isTokenValid(loginUser)) {
// Token 过期,刷新缓存
tokenService.verifyToken(loginUser);
}
if (StringUtils.isNotNull(loginUser)) {
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
}
chain.doFilter(request, response);
}
}