作者 RuoYi

新增在线用户

  1 +import request from '@/utils/request'
  2 +
  3 +// 查询在线用户列表
  4 +export function list(query) {
  5 + return request({
  6 + url: '/monitor/online/list',
  7 + method: 'get',
  8 + params: query
  9 + })
  10 +}
  11 +
  12 +// 强退用户
  13 +export function forceLogout(tokenId) {
  14 + return request({
  15 + url: '/monitor/online/' + tokenId,
  16 + method: 'delete'
  17 + })
  18 +}
1 <template> 1 <template>
2 <div class="app-container"> 2 <div class="app-container">
3 - 在线用户 3 + <el-form :inline="true">
  4 + <el-form-item label="登录地址">
  5 + <el-input
  6 + v-model="queryParams.ipaddr"
  7 + placeholder="请输入登录地址"
  8 + clearable
  9 + size="small"
  10 + @keyup.enter.native="handleQuery"
  11 + />
  12 + </el-form-item>
  13 + <el-form-item label="用户名称">
  14 + <el-input
  15 + v-model="queryParams.userName"
  16 + placeholder="请输入用户名称"
  17 + clearable
  18 + size="small"
  19 + @keyup.enter.native="handleQuery"
  20 + />
  21 + </el-form-item>
  22 + <el-form-item>
  23 + <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
  24 + </el-form-item>
  25 + </el-form>
  26 +
  27 + <el-table
  28 + v-loading="loading"
  29 + :data="list.slice((pageNum-1)*pageSize,pageNum*pageSize)"
  30 + style="width: 100%;"
  31 + >
  32 + <el-table-column label="序号" type="index" align="center">
  33 + <template slot-scope="scope">
  34 + <span>{{(pageNum - 1) * pageSize + scope.$index + 1}}</span>
  35 + </template>
  36 + </el-table-column>
  37 + <el-table-column label="会话编号" align="center" prop="tokenId" :show-overflow-tooltip="true" />
  38 + <el-table-column label="登录名称" align="center" prop="userName" :show-overflow-tooltip="true" />
  39 + <el-table-column label="部门名称" align="center" prop="deptName" />
  40 + <el-table-column label="主机" align="center" prop="ipaddr" :show-overflow-tooltip="true" />
  41 + <el-table-column label="登录地点" align="center" prop="loginLocation" />
  42 + <el-table-column label="浏览器" align="center" prop="browser" />
  43 + <el-table-column label="操作系统" align="center" prop="os" />
  44 + <el-table-column label="登录时间" align="center" prop="loginTime" width="180">
  45 + <template slot-scope="scope">
  46 + <span>{{ parseTime(scope.row.loginTime) }}</span>
  47 + </template>
  48 + </el-table-column>
  49 + <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
  50 + <template slot-scope="scope">
  51 + <el-button
  52 + size="mini"
  53 + type="text"
  54 + icon="el-icon-delete"
  55 + @click="handleForceLogout(scope.row)"
  56 + v-hasPermi="['monitor:online:forceLogout']"
  57 + >强退</el-button>
  58 + </template>
  59 + </el-table-column>
  60 + </el-table>
  61 +
  62 + <pagination v-show="total>0" :total="total" :page.sync="pageNum" :limit.sync="pageSize" />
4 </div> 63 </div>
5 -</template>  
  64 +</template>
  65 +
  66 +<script>
  67 +import { list, forceLogout } from "@/api/monitor/online";
  68 +
  69 +export default {
  70 + data() {
  71 + return {
  72 + // 遮罩层
  73 + loading: true,
  74 + // 总条数
  75 + total: 0,
  76 + // 表格数据
  77 + list: [],
  78 + pageNum: 1,
  79 + pageSize: 10,
  80 + // 查询参数
  81 + queryParams: {
  82 + ipaddr: undefined,
  83 + userName: undefined
  84 + }
  85 + };
  86 + },
  87 + created() {
  88 + this.getList();
  89 + },
  90 + methods: {
  91 + /** 查询登录日志列表 */
  92 + getList() {
  93 + this.loading = true;
  94 + list(this.queryParams).then(response => {
  95 + this.list = response.rows;
  96 + this.total = response.total;
  97 + this.loading = false;
  98 + });
  99 + },
  100 + /** 搜索按钮操作 */
  101 + handleQuery() {
  102 + this.pageNum = 1;
  103 + this.getList();
  104 + },
  105 + /** 强退按钮操作 */
  106 + handleForceLogout(row) {
  107 + this.$confirm('是否确认强退名称为"' + row.userName + '"的数据项?', "警告", {
  108 + confirmButtonText: "确定",
  109 + cancelButtonText: "取消",
  110 + type: "warning"
  111 + }).then(function() {
  112 + return forceLogout(row.tokenId);
  113 + }).then(() => {
  114 + this.getList();
  115 + this.msgSuccess("强退成功");
  116 + }).catch(function() {});
  117 + }
  118 + }
  119 +};
  120 +</script>
  121 +
@@ -197,4 +197,15 @@ public class RedisCache @@ -197,4 +197,15 @@ public class RedisCache
197 Map<String, T> map = redisTemplate.opsForHash().entries(key); 197 Map<String, T> map = redisTemplate.opsForHash().entries(key);
198 return map; 198 return map;
199 } 199 }
  200 +
  201 + /**
  202 + * 获得缓存的基本对象列表
  203 + *
  204 + * @param pattern 字符串前缀
  205 + * @return 对象列表
  206 + */
  207 + public Collection<String> keys(String pattern)
  208 + {
  209 + return redisTemplate.keys(pattern);
  210 + }
200 } 211 }
@@ -32,6 +32,26 @@ public class LoginUser implements UserDetails @@ -32,6 +32,26 @@ public class LoginUser implements UserDetails
32 private Long expireTime; 32 private Long expireTime;
33 33
34 /** 34 /**
  35 + * 登录IP地址
  36 + */
  37 + private String ipaddr;
  38 +
  39 + /**
  40 + * 登录地点
  41 + */
  42 + private String loginLocation;
  43 +
  44 + /**
  45 + * 浏览器类型
  46 + */
  47 + private String browser;
  48 +
  49 + /**
  50 + * 操作系统
  51 + */
  52 + private String os;
  53 +
  54 + /**
35 * 权限列表 55 * 权限列表
36 */ 56 */
37 private Set<String> permissions; 57 private Set<String> permissions;
@@ -130,6 +150,46 @@ public class LoginUser implements UserDetails @@ -130,6 +150,46 @@ public class LoginUser implements UserDetails
130 this.loginTime = loginTime; 150 this.loginTime = loginTime;
131 } 151 }
132 152
  153 + public String getIpaddr()
  154 + {
  155 + return ipaddr;
  156 + }
  157 +
  158 + public void setIpaddr(String ipaddr)
  159 + {
  160 + this.ipaddr = ipaddr;
  161 + }
  162 +
  163 + public String getLoginLocation()
  164 + {
  165 + return loginLocation;
  166 + }
  167 +
  168 + public void setLoginLocation(String loginLocation)
  169 + {
  170 + this.loginLocation = loginLocation;
  171 + }
  172 +
  173 + public String getBrowser()
  174 + {
  175 + return browser;
  176 + }
  177 +
  178 + public void setBrowser(String browser)
  179 + {
  180 + this.browser = browser;
  181 + }
  182 +
  183 + public String getOs()
  184 + {
  185 + return os;
  186 + }
  187 +
  188 + public void setOs(String os)
  189 + {
  190 + this.os = os;
  191 + }
  192 +
133 public Long getExpireTime() 193 public Long getExpireTime()
134 { 194 {
135 return expireTime; 195 return expireTime;
@@ -9,9 +9,13 @@ import org.springframework.beans.factory.annotation.Value; @@ -9,9 +9,13 @@ import org.springframework.beans.factory.annotation.Value;
9 import org.springframework.stereotype.Component; 9 import org.springframework.stereotype.Component;
10 import com.ruoyi.common.constant.Constants; 10 import com.ruoyi.common.constant.Constants;
11 import com.ruoyi.common.utils.IdUtils; 11 import com.ruoyi.common.utils.IdUtils;
  12 +import com.ruoyi.common.utils.ServletUtils;
12 import com.ruoyi.common.utils.StringUtils; 13 import com.ruoyi.common.utils.StringUtils;
  14 +import com.ruoyi.common.utils.ip.AddressUtils;
  15 +import com.ruoyi.common.utils.ip.IpUtils;
13 import com.ruoyi.framework.redis.RedisCache; 16 import com.ruoyi.framework.redis.RedisCache;
14 import com.ruoyi.framework.security.LoginUser; 17 import com.ruoyi.framework.security.LoginUser;
  18 +import eu.bitwalker.useragentutils.UserAgent;
15 import io.jsonwebtoken.Claims; 19 import io.jsonwebtoken.Claims;
16 import io.jsonwebtoken.Jwts; 20 import io.jsonwebtoken.Jwts;
17 import io.jsonwebtoken.SignatureAlgorithm; 21 import io.jsonwebtoken.SignatureAlgorithm;
@@ -76,6 +80,7 @@ public class TokenService @@ -76,6 +80,7 @@ public class TokenService
76 { 80 {
77 String token = IdUtils.fastUUID(); 81 String token = IdUtils.fastUUID();
78 loginUser.setToken(token); 82 loginUser.setToken(token);
  83 + setUserAgent(loginUser);
79 refreshToken(loginUser); 84 refreshToken(loginUser);
80 85
81 Map<String, Object> claims = new HashMap<>(); 86 Map<String, Object> claims = new HashMap<>();
@@ -104,8 +109,7 @@ public class TokenService @@ -104,8 +109,7 @@ public class TokenService
104 /** 109 /**
105 * 刷新令牌有效期 110 * 刷新令牌有效期
106 * 111 *
107 - * @param token 令牌  
108 - * @return 令牌 112 + * @param loginUser 登录信息
109 */ 113 */
110 public void refreshToken(LoginUser loginUser) 114 public void refreshToken(LoginUser loginUser)
111 { 115 {
@@ -115,7 +119,22 @@ public class TokenService @@ -115,7 +119,22 @@ public class TokenService
115 String userKey = getTokenKey(loginUser.getToken()); 119 String userKey = getTokenKey(loginUser.getToken());
116 redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES); 120 redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);
117 } 121 }
118 - 122 +
  123 + /**
  124 + * 设置用户代理信息
  125 + *
  126 + * @param loginUser 登录信息
  127 + */
  128 + public void setUserAgent(LoginUser loginUser)
  129 + {
  130 + UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
  131 + String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
  132 + loginUser.setIpaddr(ip);
  133 + loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
  134 + loginUser.setBrowser(userAgent.getBrowser().getName());
  135 + loginUser.setOs(userAgent.getOperatingSystem().getName());
  136 + }
  137 +
119 /** 138 /**
120 * 从数据声明生成令牌 139 * 从数据声明生成令牌
121 * 140 *
  1 +package com.ruoyi.project.monitor.controller;
  2 +
  3 +import java.util.ArrayList;
  4 +import java.util.Collection;
  5 +import java.util.Collections;
  6 +import java.util.List;
  7 +import org.springframework.beans.factory.annotation.Autowired;
  8 +import org.springframework.security.access.prepost.PreAuthorize;
  9 +import org.springframework.web.bind.annotation.DeleteMapping;
  10 +import org.springframework.web.bind.annotation.GetMapping;
  11 +import org.springframework.web.bind.annotation.PathVariable;
  12 +import org.springframework.web.bind.annotation.RequestMapping;
  13 +import org.springframework.web.bind.annotation.RestController;
  14 +import com.ruoyi.common.constant.Constants;
  15 +import com.ruoyi.common.utils.StringUtils;
  16 +import com.ruoyi.framework.aspectj.lang.annotation.Log;
  17 +import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
  18 +import com.ruoyi.framework.redis.RedisCache;
  19 +import com.ruoyi.framework.security.LoginUser;
  20 +import com.ruoyi.framework.web.controller.BaseController;
  21 +import com.ruoyi.framework.web.domain.AjaxResult;
  22 +import com.ruoyi.framework.web.page.TableDataInfo;
  23 +import com.ruoyi.project.monitor.domain.SysUserOnline;
  24 +import com.ruoyi.project.system.service.ISysUserOnlineService;
  25 +
  26 +/**
  27 + * 在线用户监控
  28 + *
  29 + * @author ruoyi
  30 + */
  31 +@RestController
  32 +@RequestMapping("/monitor/online")
  33 +public class SysUserOnlineController extends BaseController
  34 +{
  35 + @Autowired
  36 + private ISysUserOnlineService userOnlineService;
  37 +
  38 + @Autowired
  39 + private RedisCache redisCache;
  40 +
  41 + @PreAuthorize("@ss.hasPermi('monitor:online:list')")
  42 + @GetMapping("/list")
  43 + public TableDataInfo list(String ipaddr, String userName)
  44 + {
  45 + Collection<String> keys = redisCache.keys(Constants.LOGIN_TOKEN_KEY + "*");
  46 + List<SysUserOnline> userOnlineList = new ArrayList<SysUserOnline>();
  47 + for (String key : keys)
  48 + {
  49 + LoginUser user = redisCache.getCacheObject(key);
  50 + if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName))
  51 + {
  52 + if (StringUtils.equals(ipaddr, user.getIpaddr()) && StringUtils.equals(userName, user.getUsername()))
  53 + {
  54 + userOnlineList.add(userOnlineService.selectOnlineByInfo(ipaddr, userName, user));
  55 + }
  56 + }
  57 + else if (StringUtils.isNotEmpty(ipaddr))
  58 + {
  59 + if (StringUtils.equals(ipaddr, user.getIpaddr()))
  60 + {
  61 + userOnlineList.add(userOnlineService.selectOnlineByIpaddr(ipaddr, user));
  62 + }
  63 + }
  64 + else if (StringUtils.isNotEmpty(userName) && StringUtils.isNotNull(user.getUser()))
  65 + {
  66 + if (StringUtils.equals(userName, user.getUsername()))
  67 + {
  68 + userOnlineList.add(userOnlineService.selectOnlineByUserName(userName, user));
  69 + }
  70 + }
  71 + else
  72 + {
  73 + userOnlineList.add(userOnlineService.loginUserToUserOnline(user));
  74 + }
  75 + }
  76 + Collections.reverse(userOnlineList);
  77 + userOnlineList.removeAll(Collections.singleton(null));
  78 + return getDataTable(userOnlineList);
  79 + }
  80 +
  81 + /**
  82 + * 强退用户
  83 + */
  84 + @PreAuthorize("@ss.hasPermi('monitor:online:forceLogout')")
  85 + @Log(title = "在线用户", businessType = BusinessType.DELETE)
  86 + @DeleteMapping("/{tokenId}")
  87 + public AjaxResult forceLogout(@PathVariable String tokenId)
  88 + {
  89 + redisCache.deleteObject(Constants.LOGIN_TOKEN_KEY + tokenId);
  90 + return AjaxResult.success();
  91 + }
  92 +}
  1 +package com.ruoyi.project.monitor.domain;
  2 +
  3 +/**
  4 + * 当前在线会话
  5 + *
  6 + * @author ruoyi
  7 + */
  8 +public class SysUserOnline
  9 +{
  10 + /** 会话编号 */
  11 + private String tokenId;
  12 +
  13 + /** 部门名称 */
  14 + private String deptName;
  15 +
  16 + /** 用户名称 */
  17 + private String userName;
  18 +
  19 + /** 登录IP地址 */
  20 + private String ipaddr;
  21 +
  22 + /** 登录地址 */
  23 + private String loginLocation;
  24 +
  25 + /** 浏览器类型 */
  26 + private String browser;
  27 +
  28 + /** 操作系统 */
  29 + private String os;
  30 +
  31 + /** 登录时间 */
  32 + private Long loginTime;
  33 +
  34 + public String getTokenId()
  35 + {
  36 + return tokenId;
  37 + }
  38 +
  39 + public void setTokenId(String tokenId)
  40 + {
  41 + this.tokenId = tokenId;
  42 + }
  43 +
  44 + public String getDeptName()
  45 + {
  46 + return deptName;
  47 + }
  48 +
  49 + public void setDeptName(String deptName)
  50 + {
  51 + this.deptName = deptName;
  52 + }
  53 +
  54 + public String getUserName()
  55 + {
  56 + return userName;
  57 + }
  58 +
  59 + public void setUserName(String userName)
  60 + {
  61 + this.userName = userName;
  62 + }
  63 +
  64 + public String getIpaddr()
  65 + {
  66 + return ipaddr;
  67 + }
  68 +
  69 + public void setIpaddr(String ipaddr)
  70 + {
  71 + this.ipaddr = ipaddr;
  72 + }
  73 +
  74 + public String getLoginLocation()
  75 + {
  76 + return loginLocation;
  77 + }
  78 +
  79 + public void setLoginLocation(String loginLocation)
  80 + {
  81 + this.loginLocation = loginLocation;
  82 + }
  83 +
  84 + public String getBrowser()
  85 + {
  86 + return browser;
  87 + }
  88 +
  89 + public void setBrowser(String browser)
  90 + {
  91 + this.browser = browser;
  92 + }
  93 +
  94 + public String getOs()
  95 + {
  96 + return os;
  97 + }
  98 +
  99 + public void setOs(String os)
  100 + {
  101 + this.os = os;
  102 + }
  103 +
  104 + public Long getLoginTime()
  105 + {
  106 + return loginTime;
  107 + }
  108 +
  109 + public void setLoginTime(Long loginTime)
  110 + {
  111 + this.loginTime = loginTime;
  112 + }
  113 +}
  1 +package com.ruoyi.project.system.service;
  2 +
  3 +import com.ruoyi.framework.security.LoginUser;
  4 +import com.ruoyi.project.monitor.domain.SysUserOnline;
  5 +
  6 +/**
  7 + * 在线用户 服务层
  8 + *
  9 + * @author ruoyi
  10 + */
  11 +public interface ISysUserOnlineService
  12 +{
  13 + /**
  14 + * 通过登录地址查询信息
  15 + *
  16 + * @param ipaddr 登录地址
  17 + * @param user 用户信息
  18 + * @return 在线用户信息
  19 + */
  20 + public SysUserOnline selectOnlineByIpaddr(String ipaddr, LoginUser user);
  21 +
  22 + /**
  23 + * 通过用户名称查询信息
  24 + *
  25 + * @param userName 用户名称
  26 + * @param user 用户信息
  27 + * @return 在线用户信息
  28 + */
  29 + public SysUserOnline selectOnlineByUserName(String userName, LoginUser user);
  30 +
  31 + /**
  32 + * 通过登录地址/用户名称查询信息
  33 + *
  34 + * @param ipaddr 登录地址
  35 + * @param userName 用户名称
  36 + * @param user 用户信息
  37 + * @return 在线用户信息
  38 + */
  39 + public SysUserOnline selectOnlineByInfo(String ipaddr, String userName, LoginUser user);
  40 +
  41 + /**
  42 + * 设置在线用户信息
  43 + *
  44 + * @param user 用户信息
  45 + * @return 在线用户
  46 + */
  47 + public SysUserOnline loginUserToUserOnline(LoginUser user);
  48 +}
  1 +package com.ruoyi.project.system.service.impl;
  2 +
  3 +import org.springframework.stereotype.Service;
  4 +import com.ruoyi.common.utils.StringUtils;
  5 +import com.ruoyi.framework.security.LoginUser;
  6 +import com.ruoyi.project.monitor.domain.SysUserOnline;
  7 +import com.ruoyi.project.system.service.ISysUserOnlineService;
  8 +
  9 +/**
  10 + * 在线用户 服务层处理
  11 + *
  12 + * @author ruoyi
  13 + */
  14 +@Service
  15 +public class SysUserOnlineServiceImpl implements ISysUserOnlineService
  16 +{
  17 + /**
  18 + * 通过登录地址查询信息
  19 + *
  20 + * @param ipaddr 登录地址
  21 + * @param user 用户信息
  22 + * @return 在线用户信息
  23 + */
  24 + @Override
  25 + public SysUserOnline selectOnlineByIpaddr(String ipaddr, LoginUser user)
  26 + {
  27 + if (StringUtils.equals(ipaddr, user.getIpaddr()))
  28 + {
  29 + return loginUserToUserOnline(user);
  30 + }
  31 + return null;
  32 + }
  33 +
  34 + /**
  35 + * 通过用户名称查询信息
  36 + *
  37 + * @param userName 用户名称
  38 + * @param user 用户信息
  39 + * @return 在线用户信息
  40 + */
  41 + @Override
  42 + public SysUserOnline selectOnlineByUserName(String userName, LoginUser user)
  43 + {
  44 + if (StringUtils.equals(userName, user.getUsername()))
  45 + {
  46 + return loginUserToUserOnline(user);
  47 + }
  48 + return null;
  49 + }
  50 +
  51 + /**
  52 + * 通过登录地址/用户名称查询信息
  53 + *
  54 + * @param ipaddr 登录地址
  55 + * @param userName 用户名称
  56 + * @param user 用户信息
  57 + * @return 在线用户信息
  58 + */
  59 + @Override
  60 + public SysUserOnline selectOnlineByInfo(String ipaddr, String userName, LoginUser user)
  61 + {
  62 + if (StringUtils.equals(ipaddr, user.getIpaddr()) && StringUtils.equals(userName, user.getUsername()))
  63 + {
  64 + return loginUserToUserOnline(user);
  65 + }
  66 + return null;
  67 + }
  68 +
  69 + /**
  70 + * 设置在线用户信息
  71 + *
  72 + * @param user 用户信息
  73 + * @return 在线用户
  74 + */
  75 + public SysUserOnline loginUserToUserOnline(LoginUser user)
  76 + {
  77 + if (StringUtils.isNull(user) && StringUtils.isNull(user.getUser()))
  78 + {
  79 + return null;
  80 + }
  81 + SysUserOnline sysUserOnline = new SysUserOnline();
  82 + sysUserOnline.setTokenId(user.getToken());
  83 + sysUserOnline.setUserName(user.getUsername());
  84 + sysUserOnline.setIpaddr(user.getIpaddr());
  85 + sysUserOnline.setLoginLocation(user.getLoginLocation());
  86 + sysUserOnline.setBrowser(user.getBrowser());
  87 + sysUserOnline.setOs(user.getOs());
  88 + sysUserOnline.setLoginTime(user.getLoginTime());
  89 + if (StringUtils.isNotNull(user.getUser().getDept()))
  90 + {
  91 + sysUserOnline.setDeptName(user.getUser().getDept().getDeptName());
  92 + }
  93 + return sysUserOnline;
  94 + }
  95 +}