移除 CacheConstants.ONLINE_TOKEN_KEY 记录在线用户的方式

This commit is contained in:
lizhengwei 2026-04-07 14:43:08 +08:00
parent 5308928100
commit 9e4b697c03
6 changed files with 34 additions and 67 deletions

View File

@ -12,6 +12,11 @@ public interface CacheConstants {
*/ */
String ONLINE_TOKEN_KEY = "online_tokens:"; String ONLINE_TOKEN_KEY = "online_tokens:";
/**
* 在线用户 satoken redis key
*/
String UserOnline = "UserOnline";
/** /**
* 参数管理 cache key * 参数管理 cache key
*/ */

View File

@ -13,7 +13,6 @@ import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.log.annotation.Log; import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType; import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.web.core.BaseController; import org.dromara.common.web.core.BaseController;
import org.dromara.system.saas.domain.SysSaasUserOnline; import org.dromara.system.saas.domain.SysSaasUserOnline;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@ -21,6 +20,7 @@ import org.springframework.web.bind.annotation.*;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@ -51,7 +51,11 @@ public class SysSaasUserOnlineController extends BaseController {
if (StpUtil.stpLogic.getTokenActiveTimeoutByToken(token) < -1) { if (StpUtil.stpLogic.getTokenActiveTimeoutByToken(token) < -1) {
continue; continue;
} }
userOnlineDTOList.add(RedisUtils.getCacheObject(CacheConstants.ONLINE_TOKEN_KEY + token)); // 从SaToken的会话中获取用户信息对象
UserOnlineDTO dto = (UserOnlineDTO) StpUtil.stpLogic.getTokenSessionByToken(token).get(CacheConstants.UserOnline);
if (dto != null) {
userOnlineDTOList.add(dto);
}
} }
if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName)) { if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName)) {
userOnlineDTOList = StreamUtils.filter(userOnlineDTOList, userOnline -> userOnlineDTOList = StreamUtils.filter(userOnlineDTOList, userOnline ->
@ -98,7 +102,8 @@ public class SysSaasUserOnlineController extends BaseController {
List<String> tokenIds = StpUtil.getTokenValueListByLoginId(StpUtil.getLoginIdAsString()); List<String> tokenIds = StpUtil.getTokenValueListByLoginId(StpUtil.getLoginIdAsString());
List<UserOnlineDTO> userOnlineDTOList = tokenIds.stream() List<UserOnlineDTO> userOnlineDTOList = tokenIds.stream()
.filter(token -> StpUtil.stpLogic.getTokenActiveTimeoutByToken(token) >= -1) .filter(token -> StpUtil.stpLogic.getTokenActiveTimeoutByToken(token) >= -1)
.map(token -> (UserOnlineDTO) RedisUtils.getCacheObject(CacheConstants.ONLINE_TOKEN_KEY + token)) .map(token -> (UserOnlineDTO) StpUtil.stpLogic.getTokenSessionByToken(token).get(CacheConstants.UserOnline))
.filter(Objects::nonNull)
.collect(Collectors.toList()); .collect(Collectors.toList());
//复制和处理 SysSaasUserOnline 对象列表 //复制和处理 SysSaasUserOnline 对象列表
Collections.reverse(userOnlineDTOList); Collections.reverse(userOnlineDTOList);

View File

@ -13,7 +13,6 @@ import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.log.annotation.Log; import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType; import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.web.core.BaseController; import org.dromara.common.web.core.BaseController;
import org.dromara.system.domain.SysUserOnline; import org.dromara.system.domain.SysUserOnline;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@ -21,6 +20,7 @@ import org.springframework.web.bind.annotation.*;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@ -51,7 +51,11 @@ public class SysUserOnlineController extends BaseController {
if (StpUtil.stpLogic.getTokenActiveTimeoutByToken(token) < -1) { if (StpUtil.stpLogic.getTokenActiveTimeoutByToken(token) < -1) {
continue; continue;
} }
userOnlineDTOList.add(RedisUtils.getCacheObject(CacheConstants.ONLINE_TOKEN_KEY + token)); // 从SaToken的会话中获取用户信息对象
UserOnlineDTO dto = (UserOnlineDTO) StpUtil.stpLogic.getTokenSessionByToken(token).get(CacheConstants.UserOnline);
if (dto != null) {
userOnlineDTOList.add(dto);
}
} }
if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName)) { if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName)) {
userOnlineDTOList = StreamUtils.filter(userOnlineDTOList, userOnline -> userOnlineDTOList = StreamUtils.filter(userOnlineDTOList, userOnline ->
@ -98,7 +102,8 @@ public class SysUserOnlineController extends BaseController {
List<String> tokenIds = StpUtil.getTokenValueListByLoginId(StpUtil.getLoginIdAsString()); List<String> tokenIds = StpUtil.getTokenValueListByLoginId(StpUtil.getLoginIdAsString());
List<UserOnlineDTO> userOnlineDTOList = tokenIds.stream() List<UserOnlineDTO> userOnlineDTOList = tokenIds.stream()
.filter(token -> StpUtil.stpLogic.getTokenActiveTimeoutByToken(token) >= -1) .filter(token -> StpUtil.stpLogic.getTokenActiveTimeoutByToken(token) >= -1)
.map(token -> (UserOnlineDTO) RedisUtils.getCacheObject(CacheConstants.ONLINE_TOKEN_KEY + token)) .map(token -> (UserOnlineDTO) StpUtil.stpLogic.getTokenSessionByToken(token).get(CacheConstants.UserOnline))
.filter(Objects::nonNull)
.collect(Collectors.toList()); .collect(Collectors.toList());
//复制和处理 SysUserOnline 对象列表 //复制和处理 SysUserOnline 对象列表
Collections.reverse(userOnlineDTOList); Collections.reverse(userOnlineDTOList);

View File

@ -1,10 +1,8 @@
package org.dromara.web.listener; package org.dromara.web.listener;
import cn.dev33.satoken.config.SaTokenConfig;
import cn.dev33.satoken.listener.SaTokenListener; import cn.dev33.satoken.listener.SaTokenListener;
import cn.dev33.satoken.stp.SaLoginModel; import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.http.useragent.UserAgent; import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil; import cn.hutool.http.useragent.UserAgentUtil;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@ -17,16 +15,12 @@ import org.dromara.common.core.utils.ServletUtils;
import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.ip.AddressUtils; import org.dromara.common.core.utils.ip.AddressUtils;
import org.dromara.common.log.event.LogininforEvent; import org.dromara.common.log.event.LogininforEvent;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.web.service.SysLoginService; import org.dromara.web.service.SysLoginService;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.time.Duration;
/** /**
* 用户行为 侦听器的实现 * 租户管理平台 用户行为 侦听器的实现
* *
* @author Lion Li * @author Lion Li
*/ */
@ -35,7 +29,6 @@ import java.time.Duration;
@Slf4j @Slf4j
public class UserActionListener implements SaTokenListener { public class UserActionListener implements SaTokenListener {
private final SaTokenConfig tokenConfig;
private final SysLoginService loginService; private final SysLoginService loginService;
/** /**
@ -45,6 +38,9 @@ public class UserActionListener implements SaTokenListener {
public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel) { public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel) {
UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent")); UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent"));
String ip = ServletUtils.getClientIP(); String ip = ServletUtils.getClientIP();
String username = (String) loginModel.getExtra(LoginHelper.USER_NAME_KEY);
String tenantId = (String) loginModel.getExtra(LoginHelper.TENANT_KEY);
// 将用户信息作为对象存储到SaToken的会话中
UserOnlineDTO dto = new UserOnlineDTO(); UserOnlineDTO dto = new UserOnlineDTO();
dto.setIpaddr(ip); dto.setIpaddr(ip);
dto.setLoginLocation(AddressUtils.getRealAddressByIP(ip)); dto.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
@ -52,19 +48,12 @@ public class UserActionListener implements SaTokenListener {
dto.setOs(userAgent.getOs().getName()); dto.setOs(userAgent.getOs().getName());
dto.setLoginTime(System.currentTimeMillis()); dto.setLoginTime(System.currentTimeMillis());
dto.setTokenId(tokenValue); dto.setTokenId(tokenValue);
String username = (String) loginModel.getExtra(LoginHelper.USER_NAME_KEY);
String tenantId = (String) loginModel.getExtra(LoginHelper.TENANT_KEY);
dto.setUserName(username); dto.setUserName(username);
dto.setClientKey((String) loginModel.getExtra(LoginHelper.CLIENT_KEY)); dto.setClientKey((String) loginModel.getExtra(LoginHelper.CLIENT_KEY));
dto.setDeviceType(loginModel.getDevice()); dto.setDeviceType(loginModel.getDevice());
dto.setDeptName((String) loginModel.getExtra(LoginHelper.DEPT_NAME_KEY)); dto.setDeptName((String) loginModel.getExtra(LoginHelper.DEPT_NAME_KEY));
TenantHelper.dynamic(tenantId, () -> { // 使用会话存储用户信息
if(tokenConfig.getTimeout() == -1) { StpUtil.stpLogic.getTokenSessionByToken(tokenValue).set(CacheConstants.UserOnline, dto);
RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto);
} else {
RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(tokenConfig.getTimeout()));
}
});
// 记录登录日志 // 记录登录日志
LogininforEvent logininforEvent = new LogininforEvent(); LogininforEvent logininforEvent = new LogininforEvent();
logininforEvent.setTenantId(tenantId); logininforEvent.setTenantId(tenantId);
@ -83,10 +72,6 @@ public class UserActionListener implements SaTokenListener {
*/ */
@Override @Override
public void doLogout(String loginType, Object loginId, String tokenValue) { public void doLogout(String loginType, Object loginId, String tokenValue) {
String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY));
TenantHelper.dynamic(tenantId, () -> {
RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
});
log.info("user doLogout, userId:{}, token:{}", loginId, tokenValue); log.info("user doLogout, userId:{}, token:{}", loginId, tokenValue);
} }
@ -95,10 +80,6 @@ public class UserActionListener implements SaTokenListener {
*/ */
@Override @Override
public void doKickout(String loginType, Object loginId, String tokenValue) { public void doKickout(String loginType, Object loginId, String tokenValue) {
String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY));
TenantHelper.dynamic(tenantId, () -> {
RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
});
log.info("user doKickout, userId:{}, token:{}", loginId, tokenValue); log.info("user doKickout, userId:{}, token:{}", loginId, tokenValue);
} }
@ -107,10 +88,6 @@ public class UserActionListener implements SaTokenListener {
*/ */
@Override @Override
public void doReplaced(String loginType, Object loginId, String tokenValue) { public void doReplaced(String loginType, Object loginId, String tokenValue) {
String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY));
TenantHelper.dynamic(tenantId, () -> {
RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
});
log.info("user doReplaced, userId:{}, token:{}", loginId, tokenValue); log.info("user doReplaced, userId:{}, token:{}", loginId, tokenValue);
} }

View File

@ -39,7 +39,7 @@ public class GatewayExceptionHandler implements ErrorWebExceptionHandler {
msg = "内部服务器错误"; msg = "内部服务器错误";
} }
log.error("[网关异常处理]请求路径:{},异常信息:{}", exchange.getRequest().getPath(), ex.getMessage()); log.error("[网关异常处理]请求路径:{},异常信息:{}", exchange.getRequest().getPath(), ex.getMessage(),ex);
return WebFluxUtils.webFluxResponseWriter(response, msg); return WebFluxUtils.webFluxResponseWriter(response, msg);
} }

View File

@ -1,31 +1,25 @@
package org.dromara.sso.listener; package org.dromara.sso.listener;
import cn.dev33.satoken.config.SaTokenConfig;
import cn.dev33.satoken.listener.SaTokenListener; import cn.dev33.satoken.listener.SaTokenListener;
import cn.dev33.satoken.stp.SaLoginModel; import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.http.useragent.UserAgent; import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil; import cn.hutool.http.useragent.UserAgentUtil;
import org.dromara.common.core.constant.CacheConstants;
import org.dromara.system.saas.api.model.SysSaasUserOnlineDTO; import org.dromara.system.saas.api.model.SysSaasUserOnlineDTO;
import org.dromara.sso.config.satoken.LoginSaasHelper; import org.dromara.sso.config.satoken.LoginSaasHelper;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.annotation.DubboReference;
import org.dromara.common.core.constant.CacheConstants;
import org.dromara.common.core.constant.Constants; import org.dromara.common.core.constant.Constants;
import org.dromara.common.core.utils.MessageUtils; import org.dromara.common.core.utils.MessageUtils;
import org.dromara.common.core.utils.ServletUtils; import org.dromara.common.core.utils.ServletUtils;
import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.ip.AddressUtils; import org.dromara.common.core.utils.ip.AddressUtils;
import org.dromara.common.log.event.LogininforEvent; import org.dromara.common.log.event.LogininforEvent;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.system.saas.api.RemoteUserService; import org.dromara.system.saas.api.RemoteUserService;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.time.Duration;
/** /**
* 用户行为 侦听器的实现 * 用户行为 侦听器的实现
* *
@ -36,11 +30,8 @@ import java.time.Duration;
@Slf4j @Slf4j
public class UserActionListener implements SaTokenListener { public class UserActionListener implements SaTokenListener {
private final SaTokenConfig tokenConfig;
@DubboReference @DubboReference
private RemoteUserService remoteUserService; private RemoteUserService remoteUserService;
// @DubboReference
// private RemoteMessageService remoteMessageService;
/** /**
* 每次登录时触发 * 每次登录时触发
@ -49,6 +40,9 @@ public class UserActionListener implements SaTokenListener {
public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel) { public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel) {
UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent")); UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent"));
String ip = ServletUtils.getClientIP(); String ip = ServletUtils.getClientIP();
String username = (String) loginModel.getExtra(LoginSaasHelper.USER_NAME_KEY);
String tenantId = (String) loginModel.getExtra(LoginSaasHelper.TENANT_KEY);
// 将用户信息作为对象存储到SaToken的会话中
SysSaasUserOnlineDTO userOnline = new SysSaasUserOnlineDTO(); SysSaasUserOnlineDTO userOnline = new SysSaasUserOnlineDTO();
userOnline.setIpaddr(ip); userOnline.setIpaddr(ip);
userOnline.setLoginLocation(AddressUtils.getRealAddressByIP(ip)); userOnline.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
@ -56,19 +50,12 @@ public class UserActionListener implements SaTokenListener {
userOnline.setOs(userAgent.getOs().getName()); userOnline.setOs(userAgent.getOs().getName());
userOnline.setLoginTime(System.currentTimeMillis()); userOnline.setLoginTime(System.currentTimeMillis());
userOnline.setTokenId(tokenValue); userOnline.setTokenId(tokenValue);
String username = (String) loginModel.getExtra(LoginSaasHelper.USER_NAME_KEY);
String tenantId = (String) loginModel.getExtra(LoginSaasHelper.TENANT_KEY);
userOnline.setUserName(username); userOnline.setUserName(username);
userOnline.setClientKey((String) loginModel.getExtra(LoginSaasHelper.CLIENT_KEY)); userOnline.setClientKey((String) loginModel.getExtra(LoginSaasHelper.CLIENT_KEY));
userOnline.setDeviceType(loginModel.getDevice()); userOnline.setDeviceType(loginModel.getDevice());
userOnline.setDeptName((String) loginModel.getExtra(LoginSaasHelper.DEPT_NAME_KEY)); userOnline.setDeptName((String) loginModel.getExtra(LoginSaasHelper.DEPT_NAME_KEY));
TenantHelper.dynamic(tenantId, () -> { // 使用会话存储用户信息
if (tokenConfig.getTimeout() == -1) { StpUtil.stpLogic.getTokenSessionByToken(tokenValue).set(CacheConstants.UserOnline, userOnline);
RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, userOnline);
} else {
RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, userOnline, Duration.ofSeconds(tokenConfig.getTimeout()));
}
});
// 记录登录日志 // 记录登录日志
LogininforEvent logininforEvent = new LogininforEvent(); LogininforEvent logininforEvent = new LogininforEvent();
logininforEvent.setTenantId(tenantId); logininforEvent.setTenantId(tenantId);
@ -86,10 +73,6 @@ public class UserActionListener implements SaTokenListener {
*/ */
@Override @Override
public void doLogout(String loginType, Object loginId, String tokenValue) { public void doLogout(String loginType, Object loginId, String tokenValue) {
String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginSaasHelper.TENANT_KEY));
TenantHelper.dynamic(tenantId, () -> {
RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
});
log.info("user doLogout, useId:{}, token:{}", loginId, tokenValue); log.info("user doLogout, useId:{}, token:{}", loginId, tokenValue);
} }
@ -98,10 +81,6 @@ public class UserActionListener implements SaTokenListener {
*/ */
@Override @Override
public void doKickout(String loginType, Object loginId, String tokenValue) { public void doKickout(String loginType, Object loginId, String tokenValue) {
String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginSaasHelper.TENANT_KEY));
TenantHelper.dynamic(tenantId, () -> {
RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
});
log.info("user doLogoutByLoginId, useId:{}, token:{}", loginId, tokenValue); log.info("user doLogoutByLoginId, useId:{}, token:{}", loginId, tokenValue);
} }
@ -110,10 +89,6 @@ public class UserActionListener implements SaTokenListener {
*/ */
@Override @Override
public void doReplaced(String loginType, Object loginId, String tokenValue) { public void doReplaced(String loginType, Object loginId, String tokenValue) {
String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginSaasHelper.TENANT_KEY));
TenantHelper.dynamic(tenantId, () -> {
RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
});
log.info("user doReplaced, useId:{}, token:{}", loginId, tokenValue); log.info("user doReplaced, useId:{}, token:{}", loginId, tokenValue);
} }