✨ 新增管理员代登录功能

This commit is contained in:
caojia
2026-11-04 15:20:05 +08:00
parent 93da04fe5e
commit 5cccbf510f
17 changed files with 540 additions and 18 deletions
@@ -101,7 +101,7 @@ public class LogAspect
operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255));
if (loginUser != null)
{
operLog.setOperName(loginUser.getUsername());
operLog.setOperName(loginUser.isImpersonating() ? loginUser.getImpersonatorName() : loginUser.getUsername());
SysUser currentUser = loginUser.getUser();
if (StringUtils.isNotNull(currentUser) && StringUtils.isNotNull(currentUser.getDept()))
{
@@ -122,6 +122,11 @@ public class LogAspect
operLog.setRequestMethod(ServletUtils.getRequest().getMethod());
// 处理设置注解上的参数
getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult);
if (loginUser != null && loginUser.isImpersonating())
{
String target = "代登录目标=" + loginUser.getUsername() + "(ID=" + loginUser.getUserId() + "); ";
operLog.setOperParam(StringUtils.substring(target + StringUtils.defaultString(operLog.getOperParam()), 0, PARAM_MAX_LENGTH));
}
// 设置消耗时间
operLog.setCostTime(System.currentTimeMillis() - TIME_THREADLOCAL.get());
// 保存数据库
@@ -11,10 +11,16 @@ import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bms.common.core.domain.model.LoginUser;
import com.bms.common.utils.SecurityUtils;
import com.bms.common.utils.StringUtils;
import com.bms.framework.web.service.TokenService;
import com.bms.framework.manager.AsyncManager;
import com.bms.framework.manager.factory.AsyncFactory;
import com.bms.system.domain.SysOperLog;
import com.bms.common.utils.ip.IpUtils;
/**
* token过滤器 验证token有效性
@@ -24,6 +30,7 @@ import com.bms.framework.web.service.TokenService;
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter
{
private static final Logger log = LoggerFactory.getLogger(JwtAuthenticationTokenFilter.class);
@Autowired
private TokenService tokenService;
@@ -39,6 +46,46 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
chain.doFilter(request, response);
if (loginUser == null || !loginUser.isImpersonating())
{
chain.doFilter(request, response);
return;
}
long started = System.currentTimeMillis();
boolean failed = false;
try
{
chain.doFilter(request, response);
}
catch (IOException | ServletException | RuntimeException e)
{
failed = true;
throw e;
}
finally
{
// 记录所有代登录请求的真实操作人和目标账号,不记录令牌、请求参数或响应正文。
try
{
SysOperLog accessLog = new SysOperLog();
accessLog.setTitle("代登录访问");
accessLog.setBusinessType(0);
accessLog.setOperatorType(1);
accessLog.setOperName(loginUser.getImpersonatorName());
accessLog.setOperIp(IpUtils.getIpAddr(request));
accessLog.setOperUrl(StringUtils.substring(request.getRequestURI(), 0, 255));
accessLog.setRequestMethod(request.getMethod());
accessLog.setMethod("impersonation");
accessLog.setOperParam("实际管理员ID=" + loginUser.getImpersonatorId() + ", 目标用户="
+ loginUser.getUsername() + "(ID=" + loginUser.getUserId() + "), HTTP状态=" + response.getStatus());
accessLog.setStatus(failed || response.getStatus() >= 400 ? 1 : 0);
accessLog.setCostTime(System.currentTimeMillis() - started);
AsyncManager.me().execute(AsyncFactory.recordOper(accessLog));
}
catch (Exception e)
{
log.error("记录代登录访问日志失败", e);
}
}
}
}
@@ -74,6 +74,19 @@ public class TokenService
String uuid = (String) claims.get(Constants.LOGIN_USER_KEY);
String userKey = getTokenKey(uuid);
LoginUser user = redisCache.getCacheObject(userKey);
if (user != null && user.isImpersonating())
{
LoginUser operator = StringUtils.isEmpty(user.getImpersonatorToken()) ? null
: redisCache.getCacheObject(getTokenKey(user.getImpersonatorToken()));
if (user.getImpersonationExpireTime() == null
|| user.getImpersonationExpireTime() <= System.currentTimeMillis()
|| operator == null || operator.isImpersonating()
|| !user.getImpersonatorId().equals(operator.getUserId()))
{
redisCache.deleteObject(userKey);
return null;
}
}
return user;
}
catch (Exception e)
@@ -150,10 +163,29 @@ public class TokenService
public void refreshToken(LoginUser loginUser)
{
loginUser.setLoginTime(System.currentTimeMillis());
loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE);
long expiry = loginUser.getLoginTime() + expireTime * MILLIS_MINUTE;
if (loginUser.isImpersonating())
{
if (loginUser.getImpersonationExpireTime() == null
|| loginUser.getImpersonationExpireTime() <= loginUser.getLoginTime())
{
delLoginUser(loginUser.getToken());
return;
}
expiry = Math.min(expiry, loginUser.getImpersonationExpireTime());
}
loginUser.setExpireTime(expiry);
// 根据uuid将loginUser缓存
String userKey = getTokenKey(loginUser.getToken());
redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);
if (loginUser.isImpersonating())
{
int ttlSeconds = (int) Math.max(1, (expiry - loginUser.getLoginTime() + 999) / 1000);
redisCache.setCacheObject(userKey, loginUser, ttlSeconds, TimeUnit.SECONDS);
}
else
{
redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);
}
}
/**