diff --git a/ruoyi-ui/src/api/monitor/online.js b/ruoyi-ui/src/api/monitor/online.js
new file mode 100644
index 000000000..288ebe029
--- /dev/null
+++ b/ruoyi-ui/src/api/monitor/online.js
@@ -0,0 +1,18 @@
+import request from '@/utils/request'
+
+// 查询在线用户列表
+export function list(query) {
+ return request({
+ url: '/monitor/online/list',
+ method: 'get',
+ params: query
+ })
+}
+
+// 强退用户
+export function forceLogout(tokenId) {
+ return request({
+ url: '/monitor/online/' + tokenId,
+ method: 'delete'
+ })
+}
diff --git a/ruoyi-ui/src/views/monitor/online/index.vue b/ruoyi-ui/src/views/monitor/online/index.vue
index 764613ab7..17104de77 100644
--- a/ruoyi-ui/src/views/monitor/online/index.vue
+++ b/ruoyi-ui/src/views/monitor/online/index.vue
@@ -1,5 +1,121 @@
- 在线用户
+
+
+
+
+
+
+
+
+ 搜索
+
+
+
+
+
+
+ {{(pageNum - 1) * pageSize + scope.$index + 1}}
+
+
+
+
+
+
+
+
+
+
+
+ {{ parseTime(scope.row.loginTime) }}
+
+
+
+
+ 强退
+
+
+
+
+
-
\ No newline at end of file
+
+
+
+
diff --git a/ruoyi/src/main/java/com/ruoyi/framework/redis/RedisCache.java b/ruoyi/src/main/java/com/ruoyi/framework/redis/RedisCache.java
index a290494f0..9ec754341 100644
--- a/ruoyi/src/main/java/com/ruoyi/framework/redis/RedisCache.java
+++ b/ruoyi/src/main/java/com/ruoyi/framework/redis/RedisCache.java
@@ -197,4 +197,15 @@ public class RedisCache
Map map = redisTemplate.opsForHash().entries(key);
return map;
}
+
+ /**
+ * 获得缓存的基本对象列表
+ *
+ * @param pattern 字符串前缀
+ * @return 对象列表
+ */
+ public Collection keys(String pattern)
+ {
+ return redisTemplate.keys(pattern);
+ }
}
diff --git a/ruoyi/src/main/java/com/ruoyi/framework/security/LoginUser.java b/ruoyi/src/main/java/com/ruoyi/framework/security/LoginUser.java
index 46afd2269..6b2ff0940 100644
--- a/ruoyi/src/main/java/com/ruoyi/framework/security/LoginUser.java
+++ b/ruoyi/src/main/java/com/ruoyi/framework/security/LoginUser.java
@@ -31,6 +31,26 @@ public class LoginUser implements UserDetails
*/
private Long expireTime;
+ /**
+ * 登录IP地址
+ */
+ private String ipaddr;
+
+ /**
+ * 登录地点
+ */
+ private String loginLocation;
+
+ /**
+ * 浏览器类型
+ */
+ private String browser;
+
+ /**
+ * 操作系统
+ */
+ private String os;
+
/**
* 权限列表
*/
@@ -130,6 +150,46 @@ public class LoginUser implements UserDetails
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;
diff --git a/ruoyi/src/main/java/com/ruoyi/framework/security/service/TokenService.java b/ruoyi/src/main/java/com/ruoyi/framework/security/service/TokenService.java
index 5eafd02f8..fde9bb2de 100644
--- a/ruoyi/src/main/java/com/ruoyi/framework/security/service/TokenService.java
+++ b/ruoyi/src/main/java/com/ruoyi/framework/security/service/TokenService.java
@@ -9,9 +9,13 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.utils.IdUtils;
+import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.ip.AddressUtils;
+import com.ruoyi.common.utils.ip.IpUtils;
import com.ruoyi.framework.redis.RedisCache;
import com.ruoyi.framework.security.LoginUser;
+import eu.bitwalker.useragentutils.UserAgent;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
@@ -76,6 +80,7 @@ public class TokenService
{
String token = IdUtils.fastUUID();
loginUser.setToken(token);
+ setUserAgent(loginUser);
refreshToken(loginUser);
Map claims = new HashMap<>();
@@ -104,8 +109,7 @@ public class TokenService
/**
* 刷新令牌有效期
*
- * @param token 令牌
- * @return 令牌
+ * @param loginUser 登录信息
*/
public void refreshToken(LoginUser loginUser)
{
@@ -115,7 +119,22 @@ public class TokenService
String userKey = getTokenKey(loginUser.getToken());
redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);
}
-
+
+ /**
+ * 设置用户代理信息
+ *
+ * @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());
+ }
+
/**
* 从数据声明生成令牌
*
diff --git a/ruoyi/src/main/java/com/ruoyi/project/monitor/controller/SysUserOnlineController.java b/ruoyi/src/main/java/com/ruoyi/project/monitor/controller/SysUserOnlineController.java
new file mode 100644
index 000000000..5a7301912
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/project/monitor/controller/SysUserOnlineController.java
@@ -0,0 +1,92 @@
+package com.ruoyi.project.monitor.controller;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.ruoyi.common.constant.Constants;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.framework.aspectj.lang.annotation.Log;
+import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
+import com.ruoyi.framework.redis.RedisCache;
+import com.ruoyi.framework.security.LoginUser;
+import com.ruoyi.framework.web.controller.BaseController;
+import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.framework.web.page.TableDataInfo;
+import com.ruoyi.project.monitor.domain.SysUserOnline;
+import com.ruoyi.project.system.service.ISysUserOnlineService;
+
+/**
+ * 在线用户监控
+ *
+ * @author ruoyi
+ */
+@RestController
+@RequestMapping("/monitor/online")
+public class SysUserOnlineController extends BaseController
+{
+ @Autowired
+ private ISysUserOnlineService userOnlineService;
+
+ @Autowired
+ private RedisCache redisCache;
+
+ @PreAuthorize("@ss.hasPermi('monitor:online:list')")
+ @GetMapping("/list")
+ public TableDataInfo list(String ipaddr, String userName)
+ {
+ Collection keys = redisCache.keys(Constants.LOGIN_TOKEN_KEY + "*");
+ List userOnlineList = new ArrayList();
+ for (String key : keys)
+ {
+ LoginUser user = redisCache.getCacheObject(key);
+ if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName))
+ {
+ if (StringUtils.equals(ipaddr, user.getIpaddr()) && StringUtils.equals(userName, user.getUsername()))
+ {
+ userOnlineList.add(userOnlineService.selectOnlineByInfo(ipaddr, userName, user));
+ }
+ }
+ else if (StringUtils.isNotEmpty(ipaddr))
+ {
+ if (StringUtils.equals(ipaddr, user.getIpaddr()))
+ {
+ userOnlineList.add(userOnlineService.selectOnlineByIpaddr(ipaddr, user));
+ }
+ }
+ else if (StringUtils.isNotEmpty(userName) && StringUtils.isNotNull(user.getUser()))
+ {
+ if (StringUtils.equals(userName, user.getUsername()))
+ {
+ userOnlineList.add(userOnlineService.selectOnlineByUserName(userName, user));
+ }
+ }
+ else
+ {
+ userOnlineList.add(userOnlineService.loginUserToUserOnline(user));
+ }
+ }
+ Collections.reverse(userOnlineList);
+ userOnlineList.removeAll(Collections.singleton(null));
+ return getDataTable(userOnlineList);
+ }
+
+ /**
+ * 强退用户
+ */
+ @PreAuthorize("@ss.hasPermi('monitor:online:forceLogout')")
+ @Log(title = "在线用户", businessType = BusinessType.DELETE)
+ @DeleteMapping("/{tokenId}")
+ public AjaxResult forceLogout(@PathVariable String tokenId)
+ {
+ redisCache.deleteObject(Constants.LOGIN_TOKEN_KEY + tokenId);
+ return AjaxResult.success();
+ }
+}
diff --git a/ruoyi/src/main/java/com/ruoyi/project/monitor/domain/SysUserOnline.java b/ruoyi/src/main/java/com/ruoyi/project/monitor/domain/SysUserOnline.java
new file mode 100644
index 000000000..843148b4e
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/project/monitor/domain/SysUserOnline.java
@@ -0,0 +1,113 @@
+package com.ruoyi.project.monitor.domain;
+
+/**
+ * 当前在线会话
+ *
+ * @author ruoyi
+ */
+public class SysUserOnline
+{
+ /** 会话编号 */
+ private String tokenId;
+
+ /** 部门名称 */
+ private String deptName;
+
+ /** 用户名称 */
+ private String userName;
+
+ /** 登录IP地址 */
+ private String ipaddr;
+
+ /** 登录地址 */
+ private String loginLocation;
+
+ /** 浏览器类型 */
+ private String browser;
+
+ /** 操作系统 */
+ private String os;
+
+ /** 登录时间 */
+ private Long loginTime;
+
+ public String getTokenId()
+ {
+ return tokenId;
+ }
+
+ public void setTokenId(String tokenId)
+ {
+ this.tokenId = tokenId;
+ }
+
+ public String getDeptName()
+ {
+ return deptName;
+ }
+
+ public void setDeptName(String deptName)
+ {
+ this.deptName = deptName;
+ }
+
+ public String getUserName()
+ {
+ return userName;
+ }
+
+ public void setUserName(String userName)
+ {
+ this.userName = userName;
+ }
+
+ 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 getLoginTime()
+ {
+ return loginTime;
+ }
+
+ public void setLoginTime(Long loginTime)
+ {
+ this.loginTime = loginTime;
+ }
+}
diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/service/ISysUserOnlineService.java b/ruoyi/src/main/java/com/ruoyi/project/system/service/ISysUserOnlineService.java
new file mode 100644
index 000000000..63a4a3a12
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/project/system/service/ISysUserOnlineService.java
@@ -0,0 +1,48 @@
+package com.ruoyi.project.system.service;
+
+import com.ruoyi.framework.security.LoginUser;
+import com.ruoyi.project.monitor.domain.SysUserOnline;
+
+/**
+ * 在线用户 服务层
+ *
+ * @author ruoyi
+ */
+public interface ISysUserOnlineService
+{
+ /**
+ * 通过登录地址查询信息
+ *
+ * @param ipaddr 登录地址
+ * @param user 用户信息
+ * @return 在线用户信息
+ */
+ public SysUserOnline selectOnlineByIpaddr(String ipaddr, LoginUser user);
+
+ /**
+ * 通过用户名称查询信息
+ *
+ * @param userName 用户名称
+ * @param user 用户信息
+ * @return 在线用户信息
+ */
+ public SysUserOnline selectOnlineByUserName(String userName, LoginUser user);
+
+ /**
+ * 通过登录地址/用户名称查询信息
+ *
+ * @param ipaddr 登录地址
+ * @param userName 用户名称
+ * @param user 用户信息
+ * @return 在线用户信息
+ */
+ public SysUserOnline selectOnlineByInfo(String ipaddr, String userName, LoginUser user);
+
+ /**
+ * 设置在线用户信息
+ *
+ * @param user 用户信息
+ * @return 在线用户
+ */
+ public SysUserOnline loginUserToUserOnline(LoginUser user);
+}
diff --git a/ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysUserOnlineServiceImpl.java b/ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysUserOnlineServiceImpl.java
new file mode 100644
index 000000000..756b20ef2
--- /dev/null
+++ b/ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysUserOnlineServiceImpl.java
@@ -0,0 +1,95 @@
+package com.ruoyi.project.system.service.impl;
+
+import org.springframework.stereotype.Service;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.framework.security.LoginUser;
+import com.ruoyi.project.monitor.domain.SysUserOnline;
+import com.ruoyi.project.system.service.ISysUserOnlineService;
+
+/**
+ * 在线用户 服务层处理
+ *
+ * @author ruoyi
+ */
+@Service
+public class SysUserOnlineServiceImpl implements ISysUserOnlineService
+{
+ /**
+ * 通过登录地址查询信息
+ *
+ * @param ipaddr 登录地址
+ * @param user 用户信息
+ * @return 在线用户信息
+ */
+ @Override
+ public SysUserOnline selectOnlineByIpaddr(String ipaddr, LoginUser user)
+ {
+ if (StringUtils.equals(ipaddr, user.getIpaddr()))
+ {
+ return loginUserToUserOnline(user);
+ }
+ return null;
+ }
+
+ /**
+ * 通过用户名称查询信息
+ *
+ * @param userName 用户名称
+ * @param user 用户信息
+ * @return 在线用户信息
+ */
+ @Override
+ public SysUserOnline selectOnlineByUserName(String userName, LoginUser user)
+ {
+ if (StringUtils.equals(userName, user.getUsername()))
+ {
+ return loginUserToUserOnline(user);
+ }
+ return null;
+ }
+
+ /**
+ * 通过登录地址/用户名称查询信息
+ *
+ * @param ipaddr 登录地址
+ * @param userName 用户名称
+ * @param user 用户信息
+ * @return 在线用户信息
+ */
+ @Override
+ public SysUserOnline selectOnlineByInfo(String ipaddr, String userName, LoginUser user)
+ {
+ if (StringUtils.equals(ipaddr, user.getIpaddr()) && StringUtils.equals(userName, user.getUsername()))
+ {
+ return loginUserToUserOnline(user);
+ }
+ return null;
+ }
+
+ /**
+ * 设置在线用户信息
+ *
+ * @param user 用户信息
+ * @return 在线用户
+ */
+ public SysUserOnline loginUserToUserOnline(LoginUser user)
+ {
+ if (StringUtils.isNull(user) && StringUtils.isNull(user.getUser()))
+ {
+ return null;
+ }
+ SysUserOnline sysUserOnline = new SysUserOnline();
+ sysUserOnline.setTokenId(user.getToken());
+ sysUserOnline.setUserName(user.getUsername());
+ sysUserOnline.setIpaddr(user.getIpaddr());
+ sysUserOnline.setLoginLocation(user.getLoginLocation());
+ sysUserOnline.setBrowser(user.getBrowser());
+ sysUserOnline.setOs(user.getOs());
+ sysUserOnline.setLoginTime(user.getLoginTime());
+ if (StringUtils.isNotNull(user.getUser().getDept()))
+ {
+ sysUserOnline.setDeptName(user.getUser().getDept().getDeptName());
+ }
+ return sysUserOnline;
+ }
+}