package com.fzzy.sys.controller.monitor; import com.ruoyi.common.annotation.Log; import com.ruoyi.common.constant.ShiroConstants; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.domain.entity.SysUser; import com.ruoyi.common.core.page.PageDomain; import com.ruoyi.common.core.page.TableDataInfo; import com.ruoyi.common.core.page.TableSupport; import com.ruoyi.common.enums.BusinessType; import com.ruoyi.common.utils.ShiroUtils; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.spring.SpringUtils; import com.ruoyi.system.domain.SysUserOnline; import lombok.extern.slf4j.Slf4j; import org.apache.shiro.authz.annotation.Logical; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.apache.shiro.cache.Cache; import org.apache.shiro.session.Session; import org.apache.shiro.subject.SimplePrincipalCollection; import org.apache.shiro.subject.support.DefaultSubjectContext; import org.crazycake.shiro.RedisCacheManager; import org.crazycake.shiro.RedisSessionDAO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import java.io.Serializable; import java.util.*; /** * 在线用户监控 * * @author ruoyi */ @Slf4j @Controller @RequestMapping("/monitor/online") public class SysUserOnlineController extends BaseController { private String prefix = "monitor/online"; @Autowired private RedisSessionDAO redisSessionDAO; @RequiresPermissions("monitor:online:view") @GetMapping() public String online() { return prefix + "/online"; } @RequiresPermissions("monitor:online:list") @PostMapping("/list") @ResponseBody public TableDataInfo list(SysUserOnline userOnline) { String ipaddr = userOnline.getIpaddr(); String loginName = userOnline.getLoginName(); TableDataInfo rspData = new TableDataInfo(); Collection sessions = redisSessionDAO.getActiveSessions(); Iterator it = sessions.iterator(); List sessionList = new ArrayList(); long currentTime = System.currentTimeMillis(); while (it.hasNext()) { Session session = it.next(); // 检查 Session 是否过期 if (isSessionExpired(session, currentTime)) { continue; } SysUserOnline user = getSession(session); if (StringUtils.isNotNull(user)) { if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(loginName)) { if (StringUtils.equals(ipaddr, user.getIpaddr()) && StringUtils.equals(loginName, user.getLoginName())) { sessionList.add(user); } } else if (StringUtils.isNotEmpty(ipaddr)) { if (StringUtils.equals(ipaddr, user.getIpaddr())) { sessionList.add(user); } } else if (StringUtils.isNotEmpty(loginName)) { if (StringUtils.equals(loginName, user.getLoginName())) { sessionList.add(user); } } else { sessionList.add(user); } } } // 对 sessionList 进行分页 PageDomain pageDomain = TableSupport.buildPageRequest(); Integer pageNum = pageDomain.getPageNum(); Integer pageSize = pageDomain.getPageSize(); int total = sessionList.size(); int fromIndex = (pageNum - 1) * pageSize; int toIndex = Math.min(fromIndex + pageSize, total); if (fromIndex < total) { sessionList = sessionList.subList(fromIndex, toIndex); } else { sessionList = new ArrayList<>(); } rspData.setRows(sessionList); rspData.setTotal(total); return rspData; } /** * 检查 Session 是否已过期 */ private boolean isSessionExpired(Session session, long currentTime) { if (session == null) { return true; } Long timeout = session.getTimeout(); if (timeout == null || timeout <= 0) { timeout = 1800000L; // 默认 30 分钟 } long lastAccessTime = session.getLastAccessTime().getTime(); return (currentTime - lastAccessTime) > timeout; } @RequiresPermissions(value = {"monitor:online:batchForceLogout", "monitor:online:forceLogout"}, logical = Logical.OR) @Log(title = "在线用户", businessType = BusinessType.FORCE) @PostMapping("/batchForceLogout") @ResponseBody public AjaxResult batchForceLogout(String ids) { if (StringUtils.isBlank(ids)) { return error("参数不能为空"); } String[] sessionIds = ids.split(","); for (String sessionId : sessionIds) { try { Session session = redisSessionDAO.readSession(sessionId); if (session != null) { if (sessionId.equals(ShiroUtils.getSessionId())) { return error("当前登录用户无法强退"); } redisSessionDAO.delete(session); Object obj = session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY); if (obj instanceof SimplePrincipalCollection) { SimplePrincipalCollection spc = (SimplePrincipalCollection) obj; Object principal = spc.getPrimaryPrincipal(); if (principal instanceof SysUser) { SysUser sysUser = (SysUser) principal; removeUserCache(sysUser.getLoginName(), sessionId); } } } } catch (Exception e) { log.error("强退用户失败,sessionId: {}", sessionId, e); } } return success(); } private SysUserOnline getSession(Session session) { Object obj = session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY); if (null == obj) { return null; } if (obj instanceof SimplePrincipalCollection) { SimplePrincipalCollection spc = (SimplePrincipalCollection) obj; obj = spc.getPrimaryPrincipal(); if (null != obj && obj instanceof SysUser) { SysUser sysUser = (SysUser) obj; SysUserOnline userOnline = new SysUserOnline(); userOnline.setSessionId(session.getId().toString()); userOnline.setLoginName(sysUser.getLoginName()); if (StringUtils.isNotNull(sysUser.getDept()) && StringUtils.isNotEmpty(sysUser.getDept().getDeptName())) { userOnline.setDeptName(sysUser.getDept().getDeptName()); } userOnline.setIpaddr(session.getHost()); userOnline.setStartTimestamp(session.getStartTimestamp()); userOnline.setLastAccessTime(session.getLastAccessTime()); return userOnline; } } return null; } public void removeUserCache(String loginName, String sessionId) { Cache> cache = SpringUtils.getBean(RedisCacheManager.class).getCache(ShiroConstants.SYS_USERCACHE); Deque deque = cache.get(loginName); if (StringUtils.isEmpty(deque) || deque.size() == 0) { return; } deque.remove(sessionId); cache.put(loginName, deque); } }