From 0a377aab608f417e2c087abbfde5228b2766575c Mon Sep 17 00:00:00 2001 From: youjianchi Date: Mon, 20 Nov 2023 19:09:31 +0800 Subject: [PATCH] =?UTF-8?q?[add]:=E6=95=B4=E5=90=88redis?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../monitor/SysUserOnlineController.java | 135 ++++++-- .../src/main/resources/application-druid.yml | 13 + .../main/resources/ehcache/ehcache-shiro.xml | 91 ----- .../templates/monitor/online/online.html | 82 +++-- ruoyi-common/pom.xml | 21 +- .../ruoyi/common/core/redis/RedisCache.java | 316 ++++++++++++++++++ .../com/ruoyi/common/utils/CacheUtils.java | 22 +- .../com/ruoyi/common/utils/DictUtils.java | 28 +- .../config/FastJson2JsonRedisSerializer.java | 71 ++++ .../ruoyi/framework/config/RedisConfig.java | 45 +++ .../ruoyi/framework/config/ShiroConfig.java | 210 +++++------- .../framework/manager/ShutdownManager.java | 50 +-- .../manager/factory/AsyncFactory.java | 34 -- .../shiro/service/SysPasswordService.java | 41 ++- .../shiro/service/SysShiroService.java | 62 ---- .../shiro/session/OnlineSessionDAO.java | 117 ------- .../shiro/web/filter/LogoutFilter.java | 29 +- .../filter/online/OnlineSessionFilter.java | 99 ------ .../filter/sync/SyncOnlineSessionFilter.java | 39 --- .../web/session/OnlineWebSessionManager.java | 175 ---------- .../SpringSessionValidationScheduler.java | 131 -------- .../framework/web/service/CacheService.java | 100 +++++- .../system/mapper/SysUserOnlineMapper.java | 52 --- .../system/service/ISysConfigService.java | 4 + .../system/service/ISysDictTypeService.java | 4 + .../system/service/ISysUserOnlineService.java | 75 ----- .../service/impl/SysConfigServiceImpl.java | 70 ++-- .../service/impl/SysDictTypeServiceImpl.java | 66 ++-- .../impl/SysUserOnlineServiceImpl.java | 141 -------- .../mapper/system/SysUserOnlineMapper.xml | 57 ---- 30 files changed, 969 insertions(+), 1411 deletions(-) delete mode 100644 ruoyi-admin/src/main/resources/ehcache/ehcache-shiro.xml create mode 100644 ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java create mode 100644 ruoyi-framework/src/main/java/com/ruoyi/framework/config/FastJson2JsonRedisSerializer.java create mode 100644 ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java delete mode 100644 ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/service/SysShiroService.java delete mode 100644 ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/session/OnlineSessionDAO.java delete mode 100644 ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/web/filter/online/OnlineSessionFilter.java delete mode 100644 ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/web/filter/sync/SyncOnlineSessionFilter.java delete mode 100644 ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/web/session/OnlineWebSessionManager.java delete mode 100644 ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/web/session/SpringSessionValidationScheduler.java delete mode 100644 ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserOnlineMapper.java delete mode 100644 ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserOnlineService.java delete mode 100644 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserOnlineServiceImpl.java delete mode 100644 ruoyi-system/src/main/resources/mapper/system/SysUserOnlineMapper.xml diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java index 19435e94..40bc9c41 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java @@ -1,26 +1,37 @@ package com.ruoyi.web.controller.monitor; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Deque; +import java.util.Iterator; import java.util.List; 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.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; 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.TableDataInfo; -import com.ruoyi.common.core.text.Convert; import com.ruoyi.common.enums.BusinessType; -import com.ruoyi.common.enums.OnlineStatus; import com.ruoyi.common.utils.ShiroUtils; -import com.ruoyi.framework.shiro.session.OnlineSession; -import com.ruoyi.framework.shiro.session.OnlineSessionDAO; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.spring.SpringUtils; import com.ruoyi.system.domain.SysUserOnline; -import com.ruoyi.system.service.ISysUserOnlineService; /** * 在线用户监控 @@ -34,10 +45,7 @@ public class SysUserOnlineController extends BaseController private String prefix = "monitor/online"; @Autowired - private ISysUserOnlineService userOnlineService; - - @Autowired - private OnlineSessionDAO onlineSessionDAO; + private RedisSessionDAO redisSessionDAO; @RequiresPermissions("monitor:online:view") @GetMapping() @@ -51,38 +59,109 @@ public class SysUserOnlineController extends BaseController @ResponseBody public TableDataInfo list(SysUserOnline userOnline) { - startPage(); - List list = userOnlineService.selectUserOnlineList(userOnline); - return getDataTable(list); + String ipaddr = userOnline.getIpaddr(); + String loginName = userOnline.getLoginName(); + TableDataInfo rspData = new TableDataInfo(); + Collection sessions = redisSessionDAO.getActiveSessions(); + Iterator it = sessions.iterator(); + List sessionList = new ArrayList(); + while (it.hasNext()) + { + SysUserOnline user = getSession(it.next()); + 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); + } + } + } + rspData.setRows(sessionList); + rspData.setTotal(sessionList.size()); + return rspData; } @RequiresPermissions(value = { "monitor:online:batchForceLogout", "monitor:online:forceLogout" }, logical = Logical.OR) @Log(title = "在线用户", businessType = BusinessType.FORCE) @PostMapping("/batchForceLogout") @ResponseBody - public AjaxResult batchForceLogout(String ids) + public AjaxResult batchForceLogout(@RequestBody List sysUserOnlines) { - for (String sessionId : Convert.toStrArray(ids)) + for (SysUserOnline userOnline : sysUserOnlines) { - SysUserOnline online = userOnlineService.selectOnlineById(sessionId); - if (online == null) - { - return error("用户已下线"); - } - OnlineSession onlineSession = (OnlineSession) onlineSessionDAO.readSession(online.getSessionId()); - if (onlineSession == null) - { - return error("用户已下线"); - } + String sessionId = userOnline.getSessionId(); + String loginName = userOnline.getLoginName(); if (sessionId.equals(ShiroUtils.getSessionId())) { return error("当前登录用户无法强退"); } - onlineSessionDAO.delete(onlineSession); - online.setStatus(OnlineStatus.off_line); - userOnlineService.saveOnline(online); - userOnlineService.removeUserCache(online.getLoginName(), sessionId); + redisSessionDAO.delete(redisSessionDAO.readSession(sessionId)); + removeUserCache(loginName, sessionId); } 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); + } } diff --git a/ruoyi-admin/src/main/resources/application-druid.yml b/ruoyi-admin/src/main/resources/application-druid.yml index 5bbd5c31..69151292 100644 --- a/ruoyi-admin/src/main/resources/application-druid.yml +++ b/ruoyi-admin/src/main/resources/application-druid.yml @@ -1,5 +1,18 @@ # 数据源配置 spring: + # redis配置 + redis: + database: 0 + host: 47.116.8.180 + port: 6379 + password: Read-in2023 + timeout: 6000ms # 连接超时时长(毫秒) + lettuce: + pool: + max-active: 1000 # 连接池最大连接数(使用负值表示没有限制) + max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制) + max-idle: 10 # 连接池中的最大空闲连接 + min-idle: 5 # 连接池中的最小空闲连接 datasource: type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.cj.jdbc.Driver diff --git a/ruoyi-admin/src/main/resources/ehcache/ehcache-shiro.xml b/ruoyi-admin/src/main/resources/ehcache/ehcache-shiro.xml deleted file mode 100644 index 7bf080fc..00000000 --- a/ruoyi-admin/src/main/resources/ehcache/ehcache-shiro.xml +++ /dev/null @@ -1,91 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/templates/monitor/online/online.html b/ruoyi-admin/src/main/resources/templates/monitor/online/online.html index 158126e2..8abafa9c 100644 --- a/ruoyi-admin/src/main/resources/templates/monitor/online/online.html +++ b/ruoyi-admin/src/main/resources/templates/monitor/online/online.html @@ -44,6 +44,7 @@ $(function() { var options = { + sidePagination: "client", uniqueId: "sessionId", url: prefix + "/list", exportUrl: prefix + "/export", @@ -80,30 +81,6 @@ field: 'ipaddr', title: '主机' }, - { - field: 'loginLocation', - title: '登录地点' - }, - { - field: 'browser', - title: '浏览器' - }, - { - field: 'os', - title: '操作系统' - }, - { - field: 'status', - title: '会话状态', - align: 'center', - formatter: function(value, row, index) { - if (value == 'on_line') { - return '在线'; - } else if (value == 'off_line') { - return '离线'; - } - } - }, { field: 'startTimestamp', title: '登录时间', @@ -118,7 +95,7 @@ title: '操作', align: 'center', formatter: function(value, row, index) { - var msg = '强退 '; + var msg = '强退 '; return msg; } }] @@ -127,10 +104,31 @@ }); // 单条强退 - function forceLogout(sessionId) { + function forceLogout(sessionId, loginName) { $.modal.confirm("确定要强制选中用户下线吗?", function() { - var data = { "ids": sessionId }; - $.operate.post(prefix + "/batchForceLogout", data); + var data = []; + var session = {}; + session.sessionId = sessionId; + session.loginName = loginName; + data.push(session); + $.ajax({ + url: prefix + "/batchForceLogout", + method: 'POST', + data: JSON.stringify(data), + headers: { + 'Content-Type': 'application/json;charset=utf8' + }, + dataType: "json", + beforeSend: function() { + $.modal.loading("正在处理中,请稍后..."); + }, + success: function(result) { + if (typeof callback == "function") { + callback(result); + } + $.operate.ajaxSuccess(result); + }   + }); }) } @@ -142,9 +140,31 @@ return; } $.modal.confirm("确认要强退选中的" + rows.length + "条数据吗?", function() { - var url = prefix + "/batchForceLogout"; - var data = { "ids": rows.join() }; - $.operate.post(url, data); + var data = []; + $.map($("#" + table.options.id).bootstrapTable('getSelections'), function (row) { + var session = {}; + session.sessionId = row.sessionId; + session.loginName = row.loginName; + data.push(session); + }); + $.ajax({ + url: prefix + "/batchForceLogout", + method: 'POST', + data: JSON.stringify(data), + headers: { + 'Content-Type': 'application/json;charset=utf8' + }, + dataType: "json", + beforeSend: function() { + $.modal.loading("正在处理中,请稍后..."); + }, + success: function(result) { + if (typeof callback == "function") { + callback(result); + } + $.operate.ajaxSuccess(result); + }   + }); }); } diff --git a/ruoyi-common/pom.xml b/ruoyi-common/pom.xml index b46e4b17..5c0cb2bd 100644 --- a/ruoyi-common/pom.xml +++ b/ruoyi-common/pom.xml @@ -48,11 +48,24 @@ org.apache.shiro shiro-core - - + + - org.apache.shiro - shiro-ehcache + org.crazycake + shiro-redis + 3.3.1 + + + org.apache.velocity + velocity + + + + + + + org.springframework.boot + spring-boot-starter-data-redis diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java new file mode 100644 index 00000000..98d30820 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java @@ -0,0 +1,316 @@ +package com.ruoyi.common.core.redis; + +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.concurrent.TimeUnit; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.BoundSetOperations; +import org.springframework.data.redis.core.HashOperations; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ValueOperations; +import org.springframework.stereotype.Component; + +/** + * spring redis 工具类 + * + * @author ruoyi + **/ +@SuppressWarnings(value = { "unchecked", "rawtypes" }) +@Component +public class RedisCache +{ + @Autowired + public RedisTemplate redisTemplate; + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + */ + public void setCacheObject(final String key, final T value) + { + redisTemplate.opsForValue().set(key, value); + } + + /** + * 根据key获取缓存中的val+1 + * + * @param key 缓存的值 + * @return 数值 + */ + public Long increment(final String key) + { + return redisTemplate.boundValueOps(key).increment(); + } + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + * @param timeout 时间 + * @param timeUnit 时间颗粒度 + */ + public void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) + { + redisTemplate.opsForValue().set(key, value, timeout, timeUnit); + } + + /** + * 设置有效时间 + * + * @param key Redis键 + * @param timeout 超时时间 + * @return true=设置成功;false=设置失败 + */ + public boolean expire(final String key, final long timeout) + { + return expire(key, timeout, TimeUnit.SECONDS); + } + + /** + * 设置有效时间 + * + * @param key Redis键 + * @param timeout 超时时间 + * @param unit 时间单位 + * @return true=设置成功;false=设置失败 + */ + public boolean expire(final String key, final long timeout, final TimeUnit unit) + { + return redisTemplate.expire(key, timeout, unit); + } + + /** + * 获得缓存的基本对象。 + * + * @param key 缓存键值 + * @return 缓存键值对应的数据 + */ + public T getCacheObject(final String key) + { + ValueOperations operation = redisTemplate.opsForValue(); + return operation.get(key); + } + + /** + * 删除单个对象 + * + * @param key + */ + public boolean deleteObject(final String key) + { + return redisTemplate.delete(key); + } + + /** + * 删除集合对象 + * + * @param collection 多个对象 + * @return + */ + public long deleteObject(final Collection collection) + { + return redisTemplate.delete(collection); + } + + /** + * 缓存List数据 + * + * @param key 缓存的键值 + * @param dataList 待缓存的List数据 + * @return 缓存的对象 + */ + public long setCacheList(final String key, final List dataList) + { + Long count = redisTemplate.opsForList().rightPushAll(key, dataList); + return count == null ? 0 : count; + } + + /** + * 获得缓存的list对象 + * + * @param key 缓存的键值 + * @return 缓存键值对应的数据 + */ + public List getCacheList(final String key) + { + return redisTemplate.opsForList().range(key, 0, -1); + } + + /** + * 缓存Set + * + * @param key 缓存键值 + * @param dataSet 缓存的数据 + * @return 缓存数据的对象 + */ + public BoundSetOperations setCacheSet(final String key, final Set dataSet) + { + BoundSetOperations setOperation = redisTemplate.boundSetOps(key); + Iterator it = dataSet.iterator(); + while (it.hasNext()) + { + setOperation.add(it.next()); + } + return setOperation; + } + + /** + * 获得缓存的set + * + * @param key + * @return + */ + public Set getCacheSet(final String key) + { + return redisTemplate.opsForSet().members(key); + } + + /** + * 缓存Map + * + * @param key + * @param dataMap + */ + public void setCacheMap(final String key, final Map dataMap) + { + if (dataMap != null) { + redisTemplate.opsForHash().putAll(key, dataMap); + } + } + + /** + * 获得缓存的Map + * + * @param key + * @return + */ + public Map getCacheMap(final String key) + { + return redisTemplate.opsForHash().entries(key); + } + + /** + * 往Hash中存入数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @param value 值 + */ + public void setCacheMapValue(final String key, final String hKey, final T value) + { + redisTemplate.opsForHash().put(key, hKey, value); + } + + /** + * 获取Hash中的数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @return Hash中的对象 + */ + public T getCacheMapValue(final String key, final String hKey) + { + HashOperations opsForHash = redisTemplate.opsForHash(); + return opsForHash.get(key, hKey); + } + + /** + * 获取多个Hash中的数据 + * + * @param key Redis键 + * @param hKeys Hash键集合 + * @return Hash对象集合 + */ + public List getMultiCacheMapValue(final String key, final Collection hKeys) + { + return redisTemplate.opsForHash().multiGet(key, hKeys); + } + + /** + * 获得缓存的基本对象列表 + * + * @param pattern 字符串前缀 + * @return 对象列表 + */ + public Collection keys(final String pattern) + { + return redisTemplate.keys(pattern); + } + + public boolean set(String key, Object value, long time) { + try { + if (time > 0) { + redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS); + } else { + set(key, value); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + public boolean set(String key, Object value) { + try { + redisTemplate.opsForValue().set(key, value); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + + } + + public long incr(String key, long delta) { + if (delta < 0) { + throw new RuntimeException("递增因子必须大于0"); + } + return redisTemplate.opsForValue().increment(key, delta); + } + + public Object get(String key) { + return key == null ? null : redisTemplate.opsForValue().get(key); + } + + public boolean hasKey(String key) { + try { + return redisTemplate.hasKey(key); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 生成流水号 + * @param prefix + * @return + */ + public String generateBillNo(String prefix) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); + String yearMonthDay = sdf.format(new Date()); + String codeKey = prefix+yearMonthDay; + Long no = 0L; + //获取每日流水 + if (hasKey(codeKey)) { + no = Long.valueOf(get(codeKey).toString()); + } + //新增每日流水 + else{ + //每日流水设置3天过期,避免生成过多 + if(!set(codeKey,0,60*60*24*3)){ + throw new RuntimeException("Redis新增单据编号流水失败"); + } + } + no = incr(codeKey, 1); + if (no == null) { + throw new RuntimeException("Redis单据编号流水递增失败"); + } + String serialNum = String.format("%03d",no); + return codeKey+serialNum; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/CacheUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/CacheUtils.java index ae27682d..cd5ca9b7 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/CacheUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/CacheUtils.java @@ -3,8 +3,8 @@ package com.ruoyi.common.utils; import java.util.Iterator; import java.util.Set; import org.apache.shiro.cache.Cache; -import org.apache.shiro.cache.CacheManager; -import org.apache.shiro.cache.ehcache.EhCacheManager; +import org.crazycake.shiro.RedisCache; +import org.crazycake.shiro.RedisCacheManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.ruoyi.common.utils.spring.SpringUtils; @@ -18,7 +18,7 @@ public class CacheUtils { private static Logger logger = LoggerFactory.getLogger(CacheUtils.class); - private static CacheManager cacheManager = SpringUtils.getBean(CacheManager.class); + private static RedisCacheManager cacheManager = SpringUtils.getBean(RedisCacheManager.class); private static final String SYS_CACHE = "sys-cache"; @@ -124,7 +124,7 @@ public class CacheUtils */ public static void removeAll(String cacheName) { - Cache cache = getCache(cacheName); + RedisCache cache = getCache(cacheName); Set keys = cache.keys(); for (Iterator it = keys.iterator(); it.hasNext();) { @@ -175,23 +175,13 @@ public class CacheUtils * @param cacheName * @return */ - public static Cache getCache(String cacheName) + public static RedisCache getCache(String cacheName) { Cache cache = cacheManager.getCache(cacheName); if (cache == null) { throw new RuntimeException("当前系统中没有定义“" + cacheName + "”这个缓存。"); } - return cache; - } - - /** - * 获取所有缓存 - * - * @return 缓存组 - */ - public static String[] getCacheNames() - { - return ((EhCacheManager) cacheManager).getCacheManager().getCacheNames(); + return (RedisCache) cache; } } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/DictUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/DictUtils.java index d5c324fc..02ae65d7 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/DictUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/DictUtils.java @@ -1,9 +1,12 @@ package com.ruoyi.common.utils; +import java.util.Collection; import java.util.List; import org.springframework.stereotype.Component; import com.ruoyi.common.constant.Constants; import com.ruoyi.common.core.domain.entity.SysDictData; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.utils.spring.SpringUtils; /** * 字典工具类 @@ -26,7 +29,7 @@ public class DictUtils */ public static void setDictCache(String key, List dictDatas) { - CacheUtils.put(getCacheName(), getCacheKey(key), dictDatas); + SpringUtils.getBean(RedisCache.class).setCacheObject(getCacheKey(key), dictDatas); } /** @@ -37,7 +40,7 @@ public class DictUtils */ public static List getDictCache(String key) { - Object cacheObj = CacheUtils.get(getCacheName(), getCacheKey(key)); + Object cacheObj = SpringUtils.getBean(RedisCache.class).getCacheObject(getCacheKey(key)); if (StringUtils.isNotNull(cacheObj)) { List DictDatas = StringUtils.cast(cacheObj); @@ -83,7 +86,7 @@ public class DictUtils StringBuilder propertyString = new StringBuilder(); List datas = getDictCache(dictType); - if (StringUtils.containsAny(separator, dictValue) && StringUtils.isNotEmpty(datas)) + if (StringUtils.containsAny(dictValue, separator) && StringUtils.isNotEmpty(datas)) { for (SysDictData dict : datas) { @@ -91,7 +94,7 @@ public class DictUtils { if (value.equals(dict.getDictValue())) { - propertyString.append(dict.getDictLabel() + separator); + propertyString.append(dict.getDictLabel()).append(separator); break; } } @@ -123,7 +126,7 @@ public class DictUtils StringBuilder propertyString = new StringBuilder(); List datas = getDictCache(dictType); - if (StringUtils.containsAny(separator, dictLabel) && StringUtils.isNotEmpty(datas)) + if (StringUtils.containsAny(dictLabel, separator) && StringUtils.isNotEmpty(datas)) { for (SysDictData dict : datas) { @@ -131,7 +134,7 @@ public class DictUtils { if (label.equals(dict.getDictLabel())) { - propertyString.append(dict.getDictValue() + separator); + propertyString.append(dict.getDictValue()).append(separator); break; } } @@ -150,12 +153,23 @@ public class DictUtils return StringUtils.stripEnd(propertyString.toString(), separator); } + /** + * 删除指定字典缓存 + * + * @param key 字典键 + */ + public static void removeDictCache(String key) + { + SpringUtils.getBean(RedisCache.class).deleteObject(getCacheKey(key)); + } + /** * 清空字典缓存 */ public static void clearDictCache() { - CacheUtils.removeAll(getCacheName()); + Collection keys = SpringUtils.getBean(RedisCache.class).keys(Constants.SYS_DICT_KEY + "*"); + SpringUtils.getBean(RedisCache.class).deleteObject(keys); } /** diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/FastJson2JsonRedisSerializer.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/FastJson2JsonRedisSerializer.java new file mode 100644 index 00000000..59812ea5 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/FastJson2JsonRedisSerializer.java @@ -0,0 +1,71 @@ +package com.ruoyi.framework.config; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.serializer.SerializerFeature; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.type.TypeFactory; +import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.data.redis.serializer.SerializationException; +import com.alibaba.fastjson.parser.ParserConfig; +import org.springframework.util.Assert; +import java.nio.charset.Charset; + +/** + * Redis使用FastJson序列化 + * + * @author ruoyi + */ +public class FastJson2JsonRedisSerializer implements RedisSerializer +{ + @SuppressWarnings("unused") + private ObjectMapper objectMapper = new ObjectMapper(); + + public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); + + private Class clazz; + + static + { + ParserConfig.getGlobalInstance().setAutoTypeSupport(true); + } + + public FastJson2JsonRedisSerializer(Class clazz) + { + super(); + this.clazz = clazz; + } + + @Override + public byte[] serialize(T t) throws SerializationException + { + if (t == null) + { + return new byte[0]; + } + return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET); + } + + @Override + public T deserialize(byte[] bytes) throws SerializationException + { + if (bytes == null || bytes.length <= 0) + { + return null; + } + String str = new String(bytes, DEFAULT_CHARSET); + + return JSON.parseObject(str, clazz); + } + + public void setObjectMapper(ObjectMapper objectMapper) + { + Assert.notNull(objectMapper, "'objectMapper' must not be null"); + this.objectMapper = objectMapper; + } + + protected JavaType getJavaType(Class clazz) + { + return TypeFactory.defaultInstance().constructType(clazz); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java new file mode 100644 index 00000000..a65626aa --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java @@ -0,0 +1,45 @@ +package com.ruoyi.framework.config; + +import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.StringRedisSerializer; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; + +/** + * redis配置 + * + * @author ruoyi + */ +@Configuration +@EnableCaching +public class RedisConfig extends CachingConfigurerSupport +{ + @Bean + @SuppressWarnings(value = { "unchecked", "rawtypes" }) + public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) + { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(connectionFactory); + + FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class); + + ObjectMapper mapper = new ObjectMapper(); + mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); + serializer.setObjectMapper(mapper); + + template.setValueSerializer(serializer); + // 使用StringRedisSerializer来序列化和反序列化redis的key值 + template.setKeySerializer(new StringRedisSerializer()); + template.afterPropertiesSet(); + return template; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ShiroConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ShiroConfig.java index 6c23aa7c..0cdbcc1c 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ShiroConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ShiroConfig.java @@ -1,41 +1,32 @@ package com.ruoyi.framework.config; -import at.pollux.thymeleaf.shiro.dialect.ShiroDialect; -import com.ruoyi.common.constant.Constants; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.common.utils.security.CipherUtils; -import com.ruoyi.common.utils.spring.SpringUtils; -import com.ruoyi.framework.shiro.realm.UserRealm; -import com.ruoyi.framework.shiro.session.OnlineSessionDAO; -import com.ruoyi.framework.shiro.session.OnlineSessionFactory; -import com.ruoyi.framework.shiro.web.filter.LogoutFilter; -import com.ruoyi.framework.shiro.web.filter.captcha.CaptchaValidateFilter; -import com.ruoyi.framework.shiro.web.filter.kickout.KickoutSessionFilter; -import com.ruoyi.framework.shiro.web.filter.online.OnlineSessionFilter; -import com.ruoyi.framework.shiro.web.filter.sync.SyncOnlineSessionFilter; -import com.ruoyi.framework.shiro.web.session.OnlineWebSessionManager; -import com.ruoyi.framework.shiro.web.session.SpringSessionValidationScheduler; -import org.apache.commons.io.IOUtils; -import org.apache.shiro.cache.ehcache.EhCacheManager; -import org.apache.shiro.config.ConfigurationException; -import org.apache.shiro.io.ResourceUtils; +import java.util.LinkedHashMap; +import java.util.Map; +import javax.servlet.Filter; +import org.apache.shiro.codec.Base64; import org.apache.shiro.mgt.SecurityManager; +import org.apache.shiro.session.mgt.SessionManager; import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.CookieRememberMeManager; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.apache.shiro.web.servlet.SimpleCookie; -import org.springframework.beans.factory.annotation.Qualifier; +import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; +import org.crazycake.shiro.RedisCacheManager; +import org.crazycake.shiro.RedisManager; +import org.crazycake.shiro.RedisSessionDAO; +import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; - -import javax.servlet.Filter; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.LinkedHashMap; -import java.util.Map; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.security.CipherUtils; +import com.ruoyi.framework.shiro.realm.UserRealm; +import com.ruoyi.framework.shiro.web.filter.LogoutFilter; +import com.ruoyi.framework.shiro.web.filter.captcha.CaptchaValidateFilter; +import com.ruoyi.framework.shiro.web.filter.kickout.KickoutSessionFilter; +import at.pollux.thymeleaf.shiro.dialect.ShiroDialect; /** * 权限配置加载 @@ -118,105 +109,103 @@ public class ShiroConfig private String unauthorizedUrl; /** - * 缓存管理器 使用Ehcache实现 + * 是否开启记住我功能 + */ + @Value("${shiro.rememberMe.enabled: false}") + private boolean rememberMe; + + /** + * redis缓存地址 + */ + @Value("${spring.redis.port}") + private String redisPort; + + /** + * redis缓存端口 + */ + @Value("${spring.redis.host}") + private String redisHost; + + /** + * redis数据库索引 + */ + @Value("${spring.redis.database}") + private int database; + + /** + * redis密码 + */ + @Value("${spring.redis.password}") + private String password; + + /** + * Cache Manager (shiro-redis) */ @Bean - public EhCacheManager getEhCacheManager() + public RedisCacheManager redisCacheManager() { - net.sf.ehcache.CacheManager cacheManager = net.sf.ehcache.CacheManager.getCacheManager("ruoyi"); - EhCacheManager em = new EhCacheManager(); - if (StringUtils.isNull(cacheManager)) - { - em.setCacheManager(new net.sf.ehcache.CacheManager(getCacheManagerConfigFileInputStream())); - return em; - } - else - { - em.setCacheManager(cacheManager); - return em; - } + RedisCacheManager redisCacheManager = new RedisCacheManager(); + redisCacheManager.setRedisManager(redisManager()); + redisCacheManager.setPrincipalIdFieldName("userId"); + return redisCacheManager; } /** - * 返回配置文件流 避免ehcache配置文件一直被占用,无法完全销毁项目重新部署 + * RedisManager (shiro-redis) */ - protected InputStream getCacheManagerConfigFileInputStream() + @Bean + public RedisManager redisManager() { - String configFile = "classpath:ehcache/ehcache-shiro.xml"; - InputStream inputStream = null; - try - { - inputStream = ResourceUtils.getInputStreamForPath(configFile); - byte[] b = IOUtils.toByteArray(inputStream); - InputStream in = new ByteArrayInputStream(b); - return in; - } - catch (IOException e) + RedisManager redisManager = new RedisManager(); + redisManager.setHost(redisHost + ":" + redisPort); + redisManager.setDatabase(database); + if (StringUtils.isNotEmpty(password)) { - throw new ConfigurationException( - "Unable to obtain input stream for cacheManagerConfigFile [" + configFile + "]", e); - } - finally - { - IOUtils.closeQuietly(inputStream); + redisManager.setPassword(password); } + redisManager.setTimeout(expireTime * 60); + return redisManager; } /** * 自定义Realm */ @Bean - public UserRealm userRealm(EhCacheManager cacheManager) + public UserRealm userRealm() { UserRealm userRealm = new UserRealm(); userRealm.setAuthorizationCacheName(Constants.SYS_AUTH_CACHE); - userRealm.setCacheManager(cacheManager); + userRealm.setCacheManager(redisCacheManager()); return userRealm; } /** - * 自定义sessionDAO会话 - */ - @Bean - public OnlineSessionDAO sessionDAO() - { - OnlineSessionDAO sessionDAO = new OnlineSessionDAO(); - return sessionDAO; - } - - /** - * 自定义sessionFactory会话 + * RedisSessionDAO (shiro-redis) */ @Bean - public OnlineSessionFactory sessionFactory() + public RedisSessionDAO redisSessionDAO() { - OnlineSessionFactory sessionFactory = new OnlineSessionFactory(); - return sessionFactory; + RedisSessionDAO redisSessionDAO = new RedisSessionDAO(); + redisSessionDAO.setRedisManager(redisManager()); + redisSessionDAO.setExpire(expireTime * 60); + return redisSessionDAO; } /** * 会话管理器 */ @Bean - public OnlineWebSessionManager sessionManager() + public SessionManager sessionManager() { - OnlineWebSessionManager manager = new OnlineWebSessionManager(); + DefaultWebSessionManager manager = new DefaultWebSessionManager(); // 加入缓存管理器 - manager.setCacheManager(getEhCacheManager()); - // 删除过期的session - manager.setDeleteInvalidSessions(true); - // 设置全局session超时时间 - manager.setGlobalSessionTimeout(expireTime * 60 * 1000); + manager.setCacheManager(redisCacheManager()); // 去掉 JSESSIONID manager.setSessionIdUrlRewritingEnabled(false); - // 定义要使用的无效的Session定时调度器 - manager.setSessionValidationScheduler(SpringUtils.getBean(SpringSessionValidationScheduler.class)); - // 是否定时检查session - manager.setSessionValidationSchedulerEnabled(true); // 自定义SessionDao - manager.setSessionDAO(sessionDAO()); - // 自定义sessionFactory - manager.setSessionFactory(sessionFactory()); + manager.setSessionDAO(redisSessionDAO()); + // 设置全局session超时时间 + manager.setGlobalSessionTimeout(expireTime * 60 * 1000); return manager; } @@ -230,9 +219,9 @@ public class ShiroConfig // 设置realm. securityManager.setRealm(userRealm); // 记住我 - securityManager.setRememberMeManager(rememberMeManager()); + securityManager.setRememberMeManager(rememberMe ? rememberMeManager() : null); // 注入缓存管理器; - securityManager.setCacheManager(getEhCacheManager()); + securityManager.setCacheManager(redisCacheManager()); // session管理器 securityManager.setSessionManager(sessionManager()); return securityManager; @@ -245,6 +234,7 @@ public class ShiroConfig { LogoutFilter logoutFilter = new LogoutFilter(); logoutFilter.setLoginUrl(loginUrl); + logoutFilter.setCacheManager(redisCacheManager()); return logoutFilter; } @@ -292,8 +282,6 @@ public class ShiroConfig Map filters = new LinkedHashMap(); - filters.put("onlineSession", onlineSessionFilter()); - filters.put("syncOnlineSession", syncOnlineSessionFilter()); filters.put("captchaValidate", captchaValidateFilter()); filters.put("kickout", kickoutSessionFilter()); // 注销成功,则跳转到指定页面 @@ -301,33 +289,12 @@ public class ShiroConfig shiroFilterFactoryBean.setFilters(filters); // 所有请求需要认证 - filterChainDefinitionMap.put("/**", "user,kickout,onlineSession,syncOnlineSession"); + filterChainDefinitionMap.put("/**", "user,kickout"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } - /** - * 自定义在线用户处理过滤器 - */ - public OnlineSessionFilter onlineSessionFilter() - { - OnlineSessionFilter onlineSessionFilter = new OnlineSessionFilter(); - onlineSessionFilter.setLoginUrl(loginUrl); - onlineSessionFilter.setOnlineSessionDAO(sessionDAO()); - return onlineSessionFilter; - } - - /** - * 自定义在线用户同步过滤器 - */ - public SyncOnlineSessionFilter syncOnlineSessionFilter() - { - SyncOnlineSessionFilter syncOnlineSessionFilter = new SyncOnlineSessionFilter(); - syncOnlineSessionFilter.setOnlineSessionDAO(sessionDAO()); - return syncOnlineSessionFilter; - } - /** * 自定义验证码过滤器 */ @@ -369,7 +336,7 @@ public class ShiroConfig public KickoutSessionFilter kickoutSessionFilter() { KickoutSessionFilter kickoutSessionFilter = new KickoutSessionFilter(); - kickoutSessionFilter.setCacheManager(getEhCacheManager()); + kickoutSessionFilter.setCacheManager(redisCacheManager()); kickoutSessionFilter.setSessionManager(sessionManager()); // 同一个用户最大的会话数,默认-1无限制;比如2的意思是同一个用户允许最多同时两个人登录 kickoutSessionFilter.setMaxSession(maxSession); @@ -393,11 +360,18 @@ public class ShiroConfig * 开启Shiro注解通知器 */ @Bean - public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor( - @Qualifier("securityManager") SecurityManager securityManager) + public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } + + @Bean + public static DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() + { + DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); + defaultAdvisorAutoProxyCreator.setUsePrefix(true); + return defaultAdvisorAutoProxyCreator; + } } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java index 78a4af37..e36ca3c5 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java @@ -1,55 +1,24 @@ package com.ruoyi.framework.manager; -import com.ruoyi.framework.shiro.web.session.SpringSessionValidationScheduler; -import net.sf.ehcache.CacheManager; -import org.apache.shiro.cache.ehcache.EhCacheManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.annotation.PreDestroy; /** * 确保应用退出时能关闭后台线程 * - * @author cj + * @author ruoyi */ @Component public class ShutdownManager { private static final Logger logger = LoggerFactory.getLogger("sys-user"); - @Autowired(required = false) - private SpringSessionValidationScheduler springSessionValidationScheduler; - - @Autowired(required = false) - private EhCacheManager ehCacheManager; - @PreDestroy public void destroy() { - shutdownSpringSessionValidationScheduler(); shutdownAsyncManager(); - shutdownEhCacheManager(); - } - - /** - * 停止Seesion会话检查 - */ - private void shutdownSpringSessionValidationScheduler() - { - if (springSessionValidationScheduler != null && springSessionValidationScheduler.isEnabled()) - { - try - { - logger.info("====关闭会话验证任务===="); - springSessionValidationScheduler.disableSessionValidation(); - } - catch (Exception e) - { - logger.error(e.getMessage(), e); - } - } } /** @@ -67,21 +36,4 @@ public class ShutdownManager logger.error(e.getMessage(), e); } } - - private void shutdownEhCacheManager() - { - try - { - logger.info("====关闭缓存===="); - if (ehCacheManager != null) - { - CacheManager cacheManager = ehCacheManager.getCacheManager(); - cacheManager.shutdown(); - } - } - catch (Exception e) - { - logger.error(e.getMessage(), e); - } - } } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java index befa9caa..0ed9a01f 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java @@ -10,12 +10,9 @@ import com.ruoyi.common.utils.ServletUtils; import com.ruoyi.common.utils.ShiroUtils; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.spring.SpringUtils; -import com.ruoyi.framework.shiro.session.OnlineSession; import com.ruoyi.system.domain.SysLogininfor; import com.ruoyi.system.domain.SysOperLog; -import com.ruoyi.system.domain.SysUserOnline; import com.ruoyi.system.service.ISysOperLogService; -import com.ruoyi.system.service.ISysUserOnlineService; import com.ruoyi.system.service.impl.SysLogininforServiceImpl; import eu.bitwalker.useragentutils.UserAgent; @@ -29,37 +26,6 @@ public class AsyncFactory { private static final Logger sys_user_logger = LoggerFactory.getLogger("sys-user"); - /** - * 同步session到数据库 - * - * @param session 在线用户会话 - * @return 任务task - */ - public static TimerTask syncSessionToDb(final OnlineSession session) - { - return new TimerTask() - { - @Override - public void run() - { - SysUserOnline online = new SysUserOnline(); - online.setSessionId(String.valueOf(session.getId())); - online.setDeptName(session.getDeptName()); - online.setLoginName(session.getLoginName()); - online.setStartTimestamp(session.getStartTimestamp()); - online.setLastAccessTime(session.getLastAccessTime()); - online.setExpireTime(session.getTimeout()); - online.setIpaddr(session.getHost()); - online.setLoginLocation(AddressUtils.getRealAddressByIP(session.getHost())); - online.setBrowser(session.getBrowser()); - online.setOs(session.getOs()); - online.setStatus(session.getStatus()); - SpringUtils.getBean(ISysUserOnlineService.class).saveOnline(online); - - } - }; - } - /** * 操作日志记录 * diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/service/SysPasswordService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/service/SysPasswordService.java index 0aea63f2..65fe0f17 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/service/SysPasswordService.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/service/SysPasswordService.java @@ -1,16 +1,13 @@ package com.ruoyi.framework.shiro.service; -import java.util.concurrent.atomic.AtomicInteger; -import javax.annotation.PostConstruct; -import org.apache.shiro.cache.Cache; -import org.apache.shiro.cache.CacheManager; +import java.util.concurrent.TimeUnit; import org.apache.shiro.crypto.hash.Md5Hash; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import com.ruoyi.common.constant.Constants; -import com.ruoyi.common.constant.ShiroConstants; import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.redis.RedisCache; import com.ruoyi.common.exception.user.UserPasswordNotMatchException; import com.ruoyi.common.exception.user.UserPasswordRetryLimitExceedException; import com.ruoyi.common.utils.MessageUtils; @@ -26,31 +23,40 @@ import com.ruoyi.framework.manager.factory.AsyncFactory; public class SysPasswordService { @Autowired - private CacheManager cacheManager; - - private Cache loginRecordCache; + private RedisCache redisCache; @Value(value = "${user.password.maxRetryCount}") private String maxRetryCount; - @PostConstruct - public void init() + /** + * 登录记录 cache key + */ + private final String SYS_LOGINRECORDCACHE_KEY = "sys_loginRecordCache:"; + + /** + * 设置cache key + * + * @param loginName 登录名 + * @return 缓存键key + */ + private String getCacheKey(String loginName) { - loginRecordCache = cacheManager.getCache(ShiroConstants.LOGINRECORDCACHE); + return SYS_LOGINRECORDCACHE_KEY + loginName; } public void validate(SysUser user, String password) { String loginName = user.getLoginName(); - AtomicInteger retryCount = loginRecordCache.get(loginName); + Integer retryCount = redisCache.getCacheObject(getCacheKey(loginName)); if (retryCount == null) { - retryCount = new AtomicInteger(0); - loginRecordCache.put(loginName, retryCount); + retryCount = 0; + redisCache.setCacheObject(getCacheKey(loginName), retryCount, 10, TimeUnit.MINUTES); } - if (retryCount.incrementAndGet() > Integer.valueOf(maxRetryCount).intValue()) + + if (retryCount >= Integer.valueOf(maxRetryCount).intValue()) { AsyncManager.me().execute(AsyncFactory.recordLogininfor(loginName, Constants.LOGIN_FAIL, MessageUtils.message("user.password.retry.limit.exceed", maxRetryCount))); throw new UserPasswordRetryLimitExceedException(Integer.valueOf(maxRetryCount).intValue()); @@ -58,8 +64,9 @@ public class SysPasswordService if (!matches(user, password)) { + retryCount = retryCount + 1; AsyncManager.me().execute(AsyncFactory.recordLogininfor(loginName, Constants.LOGIN_FAIL, MessageUtils.message("user.password.retry.limit.count", retryCount))); - loginRecordCache.put(loginName, retryCount); + redisCache.setCacheObject(getCacheKey(loginName), retryCount, 10, TimeUnit.MINUTES); throw new UserPasswordNotMatchException(); } else @@ -75,7 +82,7 @@ public class SysPasswordService public void clearLoginRecordCache(String loginName) { - loginRecordCache.remove(loginName); + redisCache.deleteObject(getCacheKey(loginName)); } public String encryptPassword(String loginName, String password, String salt) diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/service/SysShiroService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/service/SysShiroService.java deleted file mode 100644 index 1fb9c7e4..00000000 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/service/SysShiroService.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.ruoyi.framework.shiro.service; - -import java.io.Serializable; -import org.apache.shiro.session.Session; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.framework.shiro.session.OnlineSession; -import com.ruoyi.system.domain.SysUserOnline; -import com.ruoyi.system.service.ISysUserOnlineService; - -/** - * 会话db操作处理 - * - * @author ruoyi - */ -@Component -public class SysShiroService -{ - @Autowired - private ISysUserOnlineService onlineService; - - /** - * 删除会话 - * - * @param onlineSession 会话信息 - */ - public void deleteSession(OnlineSession onlineSession) - { - onlineService.deleteOnlineById(String.valueOf(onlineSession.getId())); - } - - /** - * 获取会话信息 - * - * @param sessionId - * @return - */ - public Session getSession(Serializable sessionId) - { - SysUserOnline userOnline = onlineService.selectOnlineById(String.valueOf(sessionId)); - return StringUtils.isNull(userOnline) ? null : createSession(userOnline); - } - - public Session createSession(SysUserOnline userOnline) - { - OnlineSession onlineSession = new OnlineSession(); - if (StringUtils.isNotNull(userOnline)) - { - onlineSession.setId(userOnline.getSessionId()); - onlineSession.setHost(userOnline.getIpaddr()); - onlineSession.setBrowser(userOnline.getBrowser()); - onlineSession.setOs(userOnline.getOs()); - onlineSession.setDeptName(userOnline.getDeptName()); - onlineSession.setLoginName(userOnline.getLoginName()); - onlineSession.setStartTimestamp(userOnline.getStartTimestamp()); - onlineSession.setLastAccessTime(userOnline.getLastAccessTime()); - onlineSession.setTimeout(userOnline.getExpireTime()); - } - return onlineSession; - } -} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/session/OnlineSessionDAO.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/session/OnlineSessionDAO.java deleted file mode 100644 index 3ee1862d..00000000 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/session/OnlineSessionDAO.java +++ /dev/null @@ -1,117 +0,0 @@ -package com.ruoyi.framework.shiro.session; - -import java.io.Serializable; -import java.util.Date; -import org.apache.shiro.session.Session; -import org.apache.shiro.session.UnknownSessionException; -import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import com.ruoyi.common.enums.OnlineStatus; -import com.ruoyi.framework.manager.AsyncManager; -import com.ruoyi.framework.manager.factory.AsyncFactory; -import com.ruoyi.framework.shiro.service.SysShiroService; - -/** - * 针对自定义的ShiroSession的db操作 - * - * @author ruoyi - */ -public class OnlineSessionDAO extends EnterpriseCacheSessionDAO -{ - /** - * 同步session到数据库的周期 单位为毫秒(默认1分钟) - */ - @Value("${shiro.session.dbSyncPeriod}") - private int dbSyncPeriod; - - /** - * 上次同步数据库的时间戳 - */ - private static final String LAST_SYNC_DB_TIMESTAMP = OnlineSessionDAO.class.getName() + "LAST_SYNC_DB_TIMESTAMP"; - - @Autowired - private SysShiroService sysShiroService; - - public OnlineSessionDAO() - { - super(); - } - - public OnlineSessionDAO(long expireTime) - { - super(); - } - - /** - * 根据会话ID获取会话 - * - * @param sessionId 会话ID - * @return ShiroSession - */ - @Override - protected Session doReadSession(Serializable sessionId) - { - return sysShiroService.getSession(sessionId); - } - - @Override - public void update(Session session) throws UnknownSessionException - { - super.update(session); - } - - /** - * 更新会话;如更新会话最后访问时间/停止会话/设置超时时间/设置移除属性等会调用 - */ - public void syncToDb(OnlineSession onlineSession) - { - Date lastSyncTimestamp = (Date) onlineSession.getAttribute(LAST_SYNC_DB_TIMESTAMP); - if (lastSyncTimestamp != null) - { - boolean needSync = true; - long deltaTime = onlineSession.getLastAccessTime().getTime() - lastSyncTimestamp.getTime(); - if (deltaTime < dbSyncPeriod * 60 * 1000) - { - // 时间差不足 无需同步 - needSync = false; - } - // isGuest = true 访客 - boolean isGuest = onlineSession.getUserId() == null || onlineSession.getUserId() == 0L; - - // session 数据变更了 同步 - if (!isGuest && onlineSession.isAttributeChanged()) - { - needSync = true; - } - - if (!needSync) - { - return; - } - } - // 更新上次同步数据库时间 - onlineSession.setAttribute(LAST_SYNC_DB_TIMESTAMP, onlineSession.getLastAccessTime()); - // 更新完后 重置标识 - if (onlineSession.isAttributeChanged()) - { - onlineSession.resetAttributeChanged(); - } - AsyncManager.me().execute(AsyncFactory.syncSessionToDb(onlineSession)); - } - - /** - * 当会话过期/停止(如用户退出时)属性等会调用 - */ - @Override - protected void doDelete(Session session) - { - OnlineSession onlineSession = (OnlineSession) session; - if (null == onlineSession) - { - return; - } - onlineSession.setStatus(OnlineStatus.off_line); - sysShiroService.deleteSession(onlineSession); - } -} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/web/filter/LogoutFilter.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/web/filter/LogoutFilter.java index c6dbfa53..205314c8 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/web/filter/LogoutFilter.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/web/filter/LogoutFilter.java @@ -1,20 +1,23 @@ package com.ruoyi.framework.shiro.web.filter; +import java.io.Serializable; +import java.util.Deque; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; +import org.apache.shiro.cache.Cache; +import org.apache.shiro.cache.CacheManager; import org.apache.shiro.session.SessionException; import org.apache.shiro.subject.Subject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.ShiroConstants; import com.ruoyi.common.core.domain.entity.SysUser; import com.ruoyi.common.utils.MessageUtils; import com.ruoyi.common.utils.ShiroUtils; import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.common.utils.spring.SpringUtils; import com.ruoyi.framework.manager.AsyncManager; import com.ruoyi.framework.manager.factory.AsyncFactory; -import com.ruoyi.system.service.ISysUserOnlineService; /** * 退出过滤器 @@ -30,6 +33,8 @@ public class LogoutFilter extends org.apache.shiro.web.filter.authc.LogoutFilter */ private String loginUrl; + private Cache> cache; + public String getLoginUrl() { return loginUrl; @@ -56,7 +61,7 @@ public class LogoutFilter extends org.apache.shiro.web.filter.authc.LogoutFilter // 记录用户退出日志 AsyncManager.me().execute(AsyncFactory.recordLogininfor(loginName, Constants.LOGOUT, MessageUtils.message("user.logout.success"))); // 清理缓存 - SpringUtils.getBean(ISysUserOnlineService.class).removeUserCache(loginName, ShiroUtils.getSessionId()); + removeUserCache(loginName, ShiroUtils.getSessionId()); } // 退出登录 subject.logout(); @@ -74,6 +79,17 @@ public class LogoutFilter extends org.apache.shiro.web.filter.authc.LogoutFilter return false; } + public void removeUserCache(String loginName, String sessionId) + { + Deque deque = cache.get(loginName); + if (StringUtils.isEmpty(deque) || deque.size() == 0) + { + return; + } + deque.remove(sessionId); + cache.put(loginName, deque); + } + /** * 退出跳转URL */ @@ -87,4 +103,11 @@ public class LogoutFilter extends org.apache.shiro.web.filter.authc.LogoutFilter } return super.getRedirectUrl(request, response, subject); } + + // 设置Cache的key的前缀 + public void setCacheManager(CacheManager cacheManager) + { + // 必须和ehcache缓存配置中的缓存name一致 + this.cache = cacheManager.getCache(ShiroConstants.SYS_USERCACHE); + } } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/web/filter/online/OnlineSessionFilter.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/web/filter/online/OnlineSessionFilter.java deleted file mode 100644 index adf5120c..00000000 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/web/filter/online/OnlineSessionFilter.java +++ /dev/null @@ -1,99 +0,0 @@ -package com.ruoyi.framework.shiro.web.filter.online; - -import java.io.IOException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import org.apache.shiro.session.Session; -import org.apache.shiro.subject.Subject; -import org.apache.shiro.web.filter.AccessControlFilter; -import org.apache.shiro.web.util.WebUtils; -import org.springframework.beans.factory.annotation.Value; -import com.ruoyi.common.constant.ShiroConstants; -import com.ruoyi.common.core.domain.entity.SysUser; -import com.ruoyi.common.enums.OnlineStatus; -import com.ruoyi.common.utils.ShiroUtils; -import com.ruoyi.framework.shiro.session.OnlineSession; -import com.ruoyi.framework.shiro.session.OnlineSessionDAO; - -/** - * 自定义访问控制 - * - * @author ruoyi - */ -public class OnlineSessionFilter extends AccessControlFilter -{ - /** - * 强制退出后重定向的地址 - */ - @Value("${shiro.user.loginUrl}") - private String loginUrl; - - private OnlineSessionDAO onlineSessionDAO; - - /** - * 表示是否允许访问;mappedValue就是[urls]配置中拦截器参数部分,如果允许访问返回true,否则false; - */ - @Override - protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) - throws Exception - { - Subject subject = getSubject(request, response); - if (subject == null || subject.getSession() == null) - { - return true; - } - Session session = onlineSessionDAO.readSession(subject.getSession().getId()); - if (session != null && session instanceof OnlineSession) - { - OnlineSession onlineSession = (OnlineSession) session; - request.setAttribute(ShiroConstants.ONLINE_SESSION, onlineSession); - // 把user对象设置进去 - boolean isGuest = onlineSession.getUserId() == null || onlineSession.getUserId() == 0L; - if (isGuest == true) - { - SysUser user = ShiroUtils.getSysUser(); - if (user != null) - { - onlineSession.setUserId(user.getUserId()); - onlineSession.setLoginName(user.getLoginName()); - onlineSession.setAvatar(user.getAvatar()); - onlineSession.setDeptName(user.getDept().getDeptName()); - onlineSession.markAttributeChanged(); - } - } - - if (onlineSession.getStatus() == OnlineStatus.off_line) - { - return false; - } - } - return true; - } - - /** - * 表示当访问拒绝时是否已经处理了;如果返回true表示需要继续处理;如果返回false表示该拦截器实例已经处理了,将直接返回即可。 - */ - @Override - protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception - { - Subject subject = getSubject(request, response); - if (subject != null) - { - subject.logout(); - } - saveRequestAndRedirectToLogin(request, response); - return false; - } - - // 跳转到登录页 - @Override - protected void redirectToLogin(ServletRequest request, ServletResponse response) throws IOException - { - WebUtils.issueRedirect(request, response, loginUrl); - } - - public void setOnlineSessionDAO(OnlineSessionDAO onlineSessionDAO) - { - this.onlineSessionDAO = onlineSessionDAO; - } -} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/web/filter/sync/SyncOnlineSessionFilter.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/web/filter/sync/SyncOnlineSessionFilter.java deleted file mode 100644 index db83cbcc..00000000 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/web/filter/sync/SyncOnlineSessionFilter.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.ruoyi.framework.shiro.web.filter.sync; - -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import org.apache.shiro.web.filter.PathMatchingFilter; -import com.ruoyi.common.constant.ShiroConstants; -import com.ruoyi.framework.shiro.session.OnlineSession; -import com.ruoyi.framework.shiro.session.OnlineSessionDAO; - -/** - * 同步Session数据到Db - * - * @author ruoyi - */ -public class SyncOnlineSessionFilter extends PathMatchingFilter -{ - private OnlineSessionDAO onlineSessionDAO; - - /** - * 同步会话数据到DB 一次请求最多同步一次 防止过多处理 需要放到Shiro过滤器之前 - */ - @Override - protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception - { - OnlineSession session = (OnlineSession) request.getAttribute(ShiroConstants.ONLINE_SESSION); - // 如果session stop了 也不同步 - // session停止时间,如果stopTimestamp不为null,则代表已停止 - if (session != null && session.getUserId() != null && session.getStopTimestamp() == null) - { - onlineSessionDAO.syncToDb(session); - } - return true; - } - - public void setOnlineSessionDAO(OnlineSessionDAO onlineSessionDAO) - { - this.onlineSessionDAO = onlineSessionDAO; - } -} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/web/session/OnlineWebSessionManager.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/web/session/OnlineWebSessionManager.java deleted file mode 100644 index 7ceebad6..00000000 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/web/session/OnlineWebSessionManager.java +++ /dev/null @@ -1,175 +0,0 @@ -package com.ruoyi.framework.shiro.web.session; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; -import java.util.List; -import org.apache.commons.lang3.time.DateUtils; -import org.apache.shiro.session.ExpiredSessionException; -import org.apache.shiro.session.InvalidSessionException; -import org.apache.shiro.session.Session; -import org.apache.shiro.session.mgt.DefaultSessionKey; -import org.apache.shiro.session.mgt.SessionKey; -import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import com.ruoyi.common.constant.ShiroConstants; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.common.utils.bean.BeanUtils; -import com.ruoyi.common.utils.spring.SpringUtils; -import com.ruoyi.framework.shiro.session.OnlineSession; -import com.ruoyi.system.domain.SysUserOnline; -import com.ruoyi.system.service.ISysUserOnlineService; - -/** - * 主要是在此如果会话的属性修改了 就标识下其修改了 然后方便 OnlineSessionDao同步 - * - * @author ruoyi - */ -public class OnlineWebSessionManager extends DefaultWebSessionManager -{ - private static final Logger log = LoggerFactory.getLogger(OnlineWebSessionManager.class); - - @Override - public void setAttribute(SessionKey sessionKey, Object attributeKey, Object value) throws InvalidSessionException - { - super.setAttribute(sessionKey, attributeKey, value); - if (value != null && needMarkAttributeChanged(attributeKey)) - { - OnlineSession session = getOnlineSession(sessionKey); - session.markAttributeChanged(); - } - } - - private boolean needMarkAttributeChanged(Object attributeKey) - { - if (attributeKey == null) - { - return false; - } - String attributeKeyStr = attributeKey.toString(); - // 优化 flash属性没必要持久化 - if (attributeKeyStr.startsWith("org.springframework")) - { - return false; - } - if (attributeKeyStr.startsWith("javax.servlet")) - { - return false; - } - if (attributeKeyStr.equals(ShiroConstants.CURRENT_USERNAME)) - { - return false; - } - return true; - } - - @Override - public Object removeAttribute(SessionKey sessionKey, Object attributeKey) throws InvalidSessionException - { - Object removed = super.removeAttribute(sessionKey, attributeKey); - if (removed != null) - { - OnlineSession s = getOnlineSession(sessionKey); - s.markAttributeChanged(); - } - - return removed; - } - - public OnlineSession getOnlineSession(SessionKey sessionKey) - { - OnlineSession session = null; - Object obj = doGetSession(sessionKey); - if (StringUtils.isNotNull(obj)) - { - session = new OnlineSession(); - BeanUtils.copyBeanProp(session, obj); - } - return session; - } - - /** - * 验证session是否有效 用于删除过期session - */ - @Override - public void validateSessions() - { - if (log.isInfoEnabled()) - { - log.info("invalidation sessions..."); - } - - int invalidCount = 0; - - int timeout = (int) this.getGlobalSessionTimeout(); - if (timeout < 0) - { - // 永不过期不进行处理 - return; - } - Date expiredDate = DateUtils.addMilliseconds(new Date(), 0 - timeout); - ISysUserOnlineService userOnlineService = SpringUtils.getBean(ISysUserOnlineService.class); - List userOnlineList = userOnlineService.selectOnlineByExpired(expiredDate); - // 批量过期删除 - List needOfflineIdList = new ArrayList(); - for (SysUserOnline userOnline : userOnlineList) - { - try - { - SessionKey key = new DefaultSessionKey(userOnline.getSessionId()); - Session session = retrieveSession(key); - if (session != null) - { - throw new InvalidSessionException(); - } - } - catch (InvalidSessionException e) - { - if (log.isDebugEnabled()) - { - boolean expired = (e instanceof ExpiredSessionException); - String msg = "Invalidated session with id [" + userOnline.getSessionId() + "]" - + (expired ? " (expired)" : " (stopped)"); - log.debug(msg); - } - invalidCount++; - needOfflineIdList.add(userOnline.getSessionId()); - userOnlineService.removeUserCache(userOnline.getLoginName(), userOnline.getSessionId()); - } - - } - if (needOfflineIdList.size() > 0) - { - try - { - userOnlineService.batchDeleteOnline(needOfflineIdList); - } - catch (Exception e) - { - log.error("batch delete db session error.", e); - } - } - - if (log.isInfoEnabled()) - { - String msg = "Finished invalidation session."; - if (invalidCount > 0) - { - msg += " [" + invalidCount + "] sessions were stopped."; - } - else - { - msg += " No sessions were stopped."; - } - log.info(msg); - } - - } - - @Override - protected Collection getActiveSessions() - { - throw new UnsupportedOperationException("getActiveSessions method not supported"); - } -} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/web/session/SpringSessionValidationScheduler.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/web/session/SpringSessionValidationScheduler.java deleted file mode 100644 index 60174c0c..00000000 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/web/session/SpringSessionValidationScheduler.java +++ /dev/null @@ -1,131 +0,0 @@ -package com.ruoyi.framework.shiro.web.session; - -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; -import org.apache.shiro.session.mgt.DefaultSessionManager; -import org.apache.shiro.session.mgt.SessionValidationScheduler; -import org.apache.shiro.session.mgt.ValidatingSessionManager; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Component; -import com.ruoyi.common.utils.Threads; - -/** - * 自定义任务调度器完成 - * - * @author ruoyi - */ -@Component -public class SpringSessionValidationScheduler implements SessionValidationScheduler -{ - private static final Logger log = LoggerFactory.getLogger(SpringSessionValidationScheduler.class); - - public static final long DEFAULT_SESSION_VALIDATION_INTERVAL = DefaultSessionManager.DEFAULT_SESSION_VALIDATION_INTERVAL; - - /** - * 定时器,用于处理超时的挂起请求,也用于连接断开时的重连。 - */ - @Autowired - @Qualifier("scheduledExecutorService") - private ScheduledExecutorService executorService; - - private volatile boolean enabled = false; - - /** - * 会话验证管理器 - */ - @Autowired - @Qualifier("sessionManager") - @Lazy - private ValidatingSessionManager sessionManager; - - // 相隔多久检查一次session的有效性,单位毫秒,默认就是10分钟 - @Value("${shiro.session.validationInterval}") - private long sessionValidationInterval; - - @Override - public boolean isEnabled() - { - return this.enabled; - } - - /** - * Specifies how frequently (in milliseconds) this Scheduler will call the - * {@link org.apache.shiro.session.mgt.ValidatingSessionManager#validateSessions() - * ValidatingSessionManager#validateSessions()} method. - * - *

- * Unless this method is called, the default value is {@link #DEFAULT_SESSION_VALIDATION_INTERVAL}. - * - * @param sessionValidationInterval - */ - public void setSessionValidationInterval(long sessionValidationInterval) - { - this.sessionValidationInterval = sessionValidationInterval; - } - - /** - * Starts session validation by creating a spring PeriodicTrigger. - */ - @Override - public void enableSessionValidation() - { - - enabled = true; - - if (log.isDebugEnabled()) - { - log.debug("Scheduling session validation job using Spring Scheduler with " - + "session validation interval of [" + sessionValidationInterval + "]ms..."); - } - - try - { - executorService.scheduleAtFixedRate(new Runnable() - { - @Override - public void run() - { - if (enabled) - { - sessionManager.validateSessions(); - } - } - }, 1000, sessionValidationInterval * 60 * 1000, TimeUnit.MILLISECONDS); - - this.enabled = true; - - if (log.isDebugEnabled()) - { - log.debug("Session validation job successfully scheduled with Spring Scheduler."); - } - - } - catch (Exception e) - { - if (log.isErrorEnabled()) - { - log.error("Error starting the Spring Scheduler session validation job. Session validation may not occur.", e); - } - } - } - - @Override - public void disableSessionValidation() - { - if (log.isDebugEnabled()) - { - log.debug("Stopping Spring Scheduler session validation job..."); - } - - if (this.enabled) - { - Threads.shutdownAndAwaitTermination(executorService); - } - this.enabled = false; - } -} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/CacheService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/CacheService.java index a8b037e4..2d3eb8ff 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/CacheService.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/CacheService.java @@ -1,10 +1,22 @@ package com.ruoyi.framework.web.service; +import java.util.Collection; +import java.util.HashSet; import java.util.Set; -import org.apache.commons.lang3.ArrayUtils; +import org.apache.shiro.authz.SimpleAuthorizationInfo; +import org.apache.shiro.session.mgt.SimpleSession; +import org.apache.shiro.subject.SimplePrincipalCollection; +import org.apache.shiro.subject.support.DefaultSubjectContext; +import org.crazycake.shiro.IRedisManager; +import org.crazycake.shiro.exception.SerializationException; +import org.crazycake.shiro.serializer.ObjectSerializer; +import org.crazycake.shiro.serializer.StringSerializer; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import com.ruoyi.common.constant.Constants; -import com.ruoyi.common.utils.CacheUtils; +import com.alibaba.fastjson.JSONObject; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.utils.StringUtils; /** * 缓存操作处理 @@ -14,6 +26,16 @@ import com.ruoyi.common.utils.CacheUtils; @Service public class CacheService { + @Autowired + private RedisCache redisCache; + + @Autowired + private IRedisManager redisManager; + + private final String DEFAULT_SESSION_KEY_PREFIX = "shiro:session:"; + + private final String DEFAULT_AUTHCACHE_KEY_PREFIX = "shiro:cache:sys-authCache"; + /** * 获取所有缓存名称 * @@ -21,8 +43,9 @@ public class CacheService */ public String[] getCacheNames() { - String[] cacheNames = CacheUtils.getCacheNames(); - return ArrayUtils.removeElement(cacheNames, Constants.SYS_AUTH_CACHE); + String[] cacheNames = { "shiro:session", "shiro:cache:sys-authCache", "shiro:cache:sys-userCache", "sys_dict", + "sys_config", "sys_loginRecordCache" }; + return cacheNames; } /** @@ -33,7 +56,13 @@ public class CacheService */ public Set getCacheKeys(String cacheName) { - return CacheUtils.getCache(cacheName).keys(); + Set tmpKeys = new HashSet(); + Collection cacheKeys = redisCache.keys(cacheName + ":*"); + for (String cacheKey : cacheKeys) + { + tmpKeys.add(cacheKey); + } + return tmpKeys; } /** @@ -45,7 +74,52 @@ public class CacheService */ public Object getCacheValue(String cacheName, String cacheKey) { - return CacheUtils.get(cacheName, cacheKey); + if (cacheKey.contains(DEFAULT_SESSION_KEY_PREFIX)) + { + try + { + SimpleSession simpleSession = (SimpleSession) new ObjectSerializer().deserialize(redisManager.get(new StringSerializer().serialize(cacheKey))); + Object obj = simpleSession.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY); + if (null == obj) + { + return "未登录会话"; + } + if (obj instanceof SimplePrincipalCollection) + { + SimplePrincipalCollection spc = (SimplePrincipalCollection) obj; + obj = spc.getPrimaryPrincipal(); + if (null != obj && obj instanceof SysUser) + { + SysUser sysUser = (SysUser) obj; + return sysUser; + } + } + return obj; + } + catch (SerializationException e) + { + return null; + } + } + if (cacheKey.contains(DEFAULT_AUTHCACHE_KEY_PREFIX)) + { + try + { + SimpleAuthorizationInfo simpleAuthorizationInfo = (SimpleAuthorizationInfo) new ObjectSerializer().deserialize(redisManager.get(new StringSerializer().serialize(cacheKey))); + JSONObject authJson = new JSONObject(); + if (StringUtils.isNotNull(simpleAuthorizationInfo)) + { + authJson.put("roles", simpleAuthorizationInfo.getRoles()); + authJson.put("permissions", simpleAuthorizationInfo.getStringPermissions()); + } + return authJson; + } + catch (SerializationException e) + { + return null; + } + } + return redisCache.getCacheObject(cacheKey); } /** @@ -55,7 +129,8 @@ public class CacheService */ public void clearCacheName(String cacheName) { - CacheUtils.removeAll(cacheName); + Collection cacheKeys = redisCache.keys(cacheName + ":*"); + redisCache.deleteObject(cacheKeys); } /** @@ -66,7 +141,7 @@ public class CacheService */ public void clearCacheKey(String cacheName, String cacheKey) { - CacheUtils.remove(cacheName, cacheKey); + redisCache.deleteObject(cacheKey); } /** @@ -74,10 +149,7 @@ public class CacheService */ public void clearAll() { - String[] cacheNames = getCacheNames(); - for (String cacheName : cacheNames) - { - CacheUtils.removeAll(cacheName); - } + Collection cacheKeys = redisCache.keys("*"); + redisCache.deleteObject(cacheKeys); } } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserOnlineMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserOnlineMapper.java deleted file mode 100644 index 5bd68edf..00000000 --- a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserOnlineMapper.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.ruoyi.system.mapper; - -import java.util.List; -import com.ruoyi.system.domain.SysUserOnline; - -/** - * 在线用户 数据层 - * - * @author ruoyi - */ -public interface SysUserOnlineMapper -{ - /** - * 通过会话序号查询信息 - * - * @param sessionId 会话ID - * @return 在线用户信息 - */ - public SysUserOnline selectOnlineById(String sessionId); - - /** - * 通过会话序号删除信息 - * - * @param sessionId 会话ID - * @return 在线用户信息 - */ - public int deleteOnlineById(String sessionId); - - /** - * 保存会话信息 - * - * @param online 会话信息 - * @return 结果 - */ - public int saveOnline(SysUserOnline online); - - /** - * 查询会话集合 - * - * @param userOnline 会话参数 - * @return 会话集合 - */ - public List selectUserOnlineList(SysUserOnline userOnline); - - /** - * 查询过期会话集合 - * - * @param lastAccessTime 过期时间 - * @return 会话集合 - */ - public List selectOnlineByExpired(String lastAccessTime); -} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysConfigService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysConfigService.java index c70db288..2379cdea 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysConfigService.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysConfigService.java @@ -70,4 +70,8 @@ public interface ISysConfigService * @return 结果 */ public String checkConfigKeyUnique(SysConfig config); + + public void resetConfigCache(); + + public void loadingConfigCache(); } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictTypeService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictTypeService.java index 900c2434..7ce368da 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictTypeService.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictTypeService.java @@ -96,4 +96,8 @@ public interface ISysDictTypeService * @return 所有字典类型 */ public List selectDictTree(SysDictType dictType); + + public void loadingDictCache(); + + public void resetDictCache(); } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserOnlineService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserOnlineService.java deleted file mode 100644 index 50c99a1c..00000000 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserOnlineService.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.ruoyi.system.service; - -import java.util.Date; -import java.util.List; -import com.ruoyi.system.domain.SysUserOnline; - -/** - * 在线用户 服务层 - * - * @author ruoyi - */ -public interface ISysUserOnlineService -{ - /** - * 通过会话序号查询信息 - * - * @param sessionId 会话ID - * @return 在线用户信息 - */ - public SysUserOnline selectOnlineById(String sessionId); - - /** - * 通过会话序号删除信息 - * - * @param sessionId 会话ID - * @return 在线用户信息 - */ - public void deleteOnlineById(String sessionId); - - /** - * 通过会话序号删除信息 - * - * @param sessions 会话ID集合 - * @return 在线用户信息 - */ - public void batchDeleteOnline(List sessions); - - /** - * 保存会话信息 - * - * @param online 会话信息 - */ - public void saveOnline(SysUserOnline online); - - /** - * 查询会话集合 - * - * @param userOnline 分页参数 - * @return 会话集合 - */ - public List selectUserOnlineList(SysUserOnline userOnline); - - /** - * 强退用户 - * - * @param sessionId 会话ID - */ - public void forceLogout(String sessionId); - - /** - * 清理用户缓存 - * - * @param loginName 登录名称 - * @param sessionId 会话ID - */ - public void removeUserCache(String loginName, String sessionId); - - /** - * 查询会话集合 - * - * @param expiredDate 有效期 - * @return 会话集合 - */ - public List selectOnlineByExpired(Date expiredDate); -} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java index 9ba91957..9f6e92f5 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java @@ -1,14 +1,16 @@ package com.ruoyi.system.service.impl; +import java.util.Collection; import java.util.List; import javax.annotation.PostConstruct; + +import com.ruoyi.common.exception.BusinessException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.ruoyi.common.constant.Constants; import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.redis.RedisCache; import com.ruoyi.common.core.text.Convert; -import com.ruoyi.common.exception.BusinessException; -import com.ruoyi.common.utils.CacheUtils; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.system.domain.SysConfig; import com.ruoyi.system.mapper.SysConfigMapper; @@ -24,6 +26,9 @@ public class SysConfigServiceImpl implements ISysConfigService { @Autowired private SysConfigMapper configMapper; + + @Autowired + private RedisCache redisCache; /** * 项目启动时,初始化参数到缓存 @@ -31,11 +36,7 @@ public class SysConfigServiceImpl implements ISysConfigService @PostConstruct public void init() { - List configsList = configMapper.selectConfigList(new SysConfig()); - for (SysConfig config : configsList) - { - CacheUtils.put(getCacheName(), getCacheKey(config.getConfigKey()), config.getConfigValue()); - } + loadingConfigCache(); } /** @@ -61,7 +62,7 @@ public class SysConfigServiceImpl implements ISysConfigService @Override public String selectConfigByKey(String configKey) { - String configValue = Convert.toStr(CacheUtils.get(getCacheName(), getCacheKey(configKey))); + String configValue = Convert.toStr(redisCache.getCacheObject(getCacheKey(configKey))); if (StringUtils.isNotEmpty(configValue)) { return configValue; @@ -71,7 +72,7 @@ public class SysConfigServiceImpl implements ISysConfigService SysConfig retConfig = configMapper.selectConfig(config); if (StringUtils.isNotNull(retConfig)) { - CacheUtils.put(getCacheName(), getCacheKey(configKey), retConfig.getConfigValue()); + redisCache.setCacheObject(getCacheKey(configKey), retConfig.getConfigValue()); return retConfig.getConfigValue(); } return StringUtils.EMPTY; @@ -101,7 +102,7 @@ public class SysConfigServiceImpl implements ISysConfigService int row = configMapper.insertConfig(config); if (row > 0) { - CacheUtils.put(getCacheName(), getCacheKey(config.getConfigKey()), config.getConfigValue()); + redisCache.setCacheObject(getCacheKey(config.getConfigKey()), config.getConfigValue()); } return row; } @@ -118,7 +119,7 @@ public class SysConfigServiceImpl implements ISysConfigService int row = configMapper.updateConfig(config); if (row > 0) { - CacheUtils.put(getCacheName(), getCacheKey(config.getConfigKey()), config.getConfigValue()); + redisCache.setCacheObject(getCacheKey(config.getConfigKey()), config.getConfigValue()); } return row; } @@ -127,11 +128,11 @@ public class SysConfigServiceImpl implements ISysConfigService * 批量删除参数配置对象 * * @param ids 需要删除的数据ID - * @return 结果 */ @Override public int deleteConfigByIds(String ids) { + int result = 0; Long[] configIds = Convert.toLongArray(ids); for (Long configId : configIds) { @@ -140,23 +141,44 @@ public class SysConfigServiceImpl implements ISysConfigService { throw new BusinessException(String.format("内置参数【%1$s】不能删除 ", config.getConfigKey())); } + int count = configMapper.deleteConfigByIds(Convert.toStrArray(ids)); + result+=count; + redisCache.deleteObject(getCacheKey(config.getConfigKey())); } - int count = configMapper.deleteConfigByIds(Convert.toStrArray(ids)); - if (count > 0) - { + return result; + } - CacheUtils.removeAll(getCacheName()); + /** + * 加载参数缓存数据 + */ + @Override + public void loadingConfigCache() + { + List configsList = configMapper.selectConfigList(new SysConfig()); + for (SysConfig config : configsList) + { + redisCache.setCacheObject(getCacheKey(config.getConfigKey()), config.getConfigValue()); } - return count; } /** - * 清空缓存数据 + * 清空参数缓存数据 */ @Override public void clearCache() { - CacheUtils.removeAll(getCacheName()); + Collection keys = redisCache.keys(Constants.SYS_CONFIG_KEY + "*"); + redisCache.deleteObject(keys); + } + + /** + * 重置参数缓存数据 + */ + @Override + public void resetConfigCache() + { + clearCache(); + loadingConfigCache(); } /** @@ -177,16 +199,6 @@ public class SysConfigServiceImpl implements ISysConfigService return UserConstants.CONFIG_KEY_UNIQUE; } - /** - * 获取cache name - * - * @return 缓存名 - */ - private String getCacheName() - { - return Constants.SYS_CONFIG_CACHE; - } - /** * 设置cache key * diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java index b3c0ffb0..047414a2 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java @@ -1,7 +1,10 @@ package com.ruoyi.system.service.impl; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; import javax.annotation.PostConstruct; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -38,12 +41,7 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService @PostConstruct public void init() { - List dictTypeList = dictTypeMapper.selectDictTypeAll(); - for (SysDictType dictType : dictTypeList) - { - List dictDatas = dictDataMapper.selectDictDataByType(dictType.getDictType()); - DictUtils.setDictCache(dictType.getDictType(), dictDatas); - } + loadingDictCache(); } /** @@ -120,11 +118,11 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService * 批量删除字典类型 * * @param ids 需要删除的数据 - * @return 结果 */ @Override public int deleteDictTypeByIds(String ids) { + int result = 0; Long[] dictIds = Convert.toLongArray(ids); for (Long dictId : dictIds) { @@ -133,17 +131,30 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService { throw new BusinessException(String.format("%1$s已分配,不能删除", dictType.getDictName())); } + int count = dictTypeMapper.deleteDictTypeById(dictId); + result+=count; + DictUtils.removeDictCache(dictType.getDictType()); } - int count = dictTypeMapper.deleteDictTypeByIds(dictIds); - if (count > 0) + return result; + } + + /** + * 加载字典缓存数据 + */ + @Override + public void loadingDictCache() + { + SysDictData dictData = new SysDictData(); + dictData.setStatus("0"); + Map> dictDataMap = dictDataMapper.selectDictDataList(dictData).stream().collect(Collectors.groupingBy(SysDictData::getDictType)); + for (Map.Entry> entry : dictDataMap.entrySet()) { - DictUtils.clearDictCache(); + DictUtils.setDictCache(entry.getKey(), entry.getValue().stream().sorted(Comparator.comparing(SysDictData::getDictSort)).collect(Collectors.toList())); } - return count; } /** - * 清空缓存数据 + * 清空字典缓存数据 */ @Override public void clearCache() @@ -151,19 +162,29 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService DictUtils.clearDictCache(); } + /** + * 重置字典缓存数据 + */ + @Override + public void resetDictCache() + { + clearCache(); + loadingDictCache(); + } + /** * 新增保存字典类型信息 * - * @param dictType 字典类型信息 + * @param dict 字典类型信息 * @return 结果 */ @Override - public int insertDictType(SysDictType dictType) + public int insertDictType(SysDictType dict) { - int row = dictTypeMapper.insertDictType(dictType); + int row = dictTypeMapper.insertDictType(dict); if (row > 0) { - DictUtils.clearDictCache(); + DictUtils.setDictCache(dict.getDictType(), null); } return row; } @@ -171,19 +192,20 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService /** * 修改保存字典类型信息 * - * @param dictType 字典类型信息 + * @param dict 字典类型信息 * @return 结果 */ @Override @Transactional - public int updateDictType(SysDictType dictType) + public int updateDictType(SysDictType dict) { - SysDictType oldDict = dictTypeMapper.selectDictTypeById(dictType.getDictId()); - dictDataMapper.updateDictDataType(oldDict.getDictType(), dictType.getDictType()); - int row = dictTypeMapper.updateDictType(dictType); + SysDictType oldDict = dictTypeMapper.selectDictTypeById(dict.getDictId()); + dictDataMapper.updateDictDataType(oldDict.getDictType(), dict.getDictType()); + int row = dictTypeMapper.updateDictType(dict); if (row > 0) { - DictUtils.clearDictCache(); + List dictDatas = dictDataMapper.selectDictDataByType(dict.getDictType()); + DictUtils.setDictCache(dict.getDictType(), dictDatas); } return row; } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserOnlineServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserOnlineServiceImpl.java deleted file mode 100644 index c170d425..00000000 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserOnlineServiceImpl.java +++ /dev/null @@ -1,141 +0,0 @@ -package com.ruoyi.system.service.impl; - -import java.io.Serializable; -import java.util.Date; -import java.util.Deque; -import java.util.List; -import org.apache.shiro.cache.Cache; -import org.apache.shiro.cache.ehcache.EhCacheManager; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import com.ruoyi.common.constant.ShiroConstants; -import com.ruoyi.common.utils.DateUtils; -import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.system.domain.SysUserOnline; -import com.ruoyi.system.mapper.SysUserOnlineMapper; -import com.ruoyi.system.service.ISysUserOnlineService; - -/** - * 在线用户 服务层处理 - * - * @author ruoyi - */ -@Service -public class SysUserOnlineServiceImpl implements ISysUserOnlineService -{ - @Autowired - private SysUserOnlineMapper userOnlineDao; - - @Autowired - private EhCacheManager ehCacheManager; - - /** - * 通过会话序号查询信息 - * - * @param sessionId 会话ID - * @return 在线用户信息 - */ - @Override - public SysUserOnline selectOnlineById(String sessionId) - { - return userOnlineDao.selectOnlineById(sessionId); - } - - /** - * 通过会话序号删除信息 - * - * @param sessionId 会话ID - * @return 在线用户信息 - */ - @Override - public void deleteOnlineById(String sessionId) - { - SysUserOnline userOnline = selectOnlineById(sessionId); - if (StringUtils.isNotNull(userOnline)) - { - userOnlineDao.deleteOnlineById(sessionId); - } - } - - /** - * 通过会话序号删除信息 - * - * @param sessions 会话ID集合 - * @return 在线用户信息 - */ - @Override - public void batchDeleteOnline(List sessions) - { - for (String sessionId : sessions) - { - SysUserOnline userOnline = selectOnlineById(sessionId); - if (StringUtils.isNotNull(userOnline)) - { - userOnlineDao.deleteOnlineById(sessionId); - } - } - } - - /** - * 保存会话信息 - * - * @param online 会话信息 - */ - @Override - public void saveOnline(SysUserOnline online) - { - userOnlineDao.saveOnline(online); - } - - /** - * 查询会话集合 - * - * @param userOnline 在线用户 - */ - @Override - public List selectUserOnlineList(SysUserOnline userOnline) - { - return userOnlineDao.selectUserOnlineList(userOnline); - } - - /** - * 强退用户 - * - * @param sessionId 会话ID - */ - @Override - public void forceLogout(String sessionId) - { - userOnlineDao.deleteOnlineById(sessionId); - } - - /** - * 清理用户缓存 - * - * @param loginName 登录名称 - * @param sessionId 会话ID - */ - @Override - public void removeUserCache(String loginName, String sessionId) - { - Cache> cache = ehCacheManager.getCache(ShiroConstants.SYS_USERCACHE); - Deque deque = cache.get(loginName); - if (StringUtils.isEmpty(deque) || deque.size() == 0) - { - return; - } - deque.remove(sessionId); - } - - /** - * 查询会话集合 - * - * @param expiredDate 失效日期 - */ - @Override - public List selectOnlineByExpired(Date expiredDate) - { - String lastAccessTime = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, expiredDate); - return userOnlineDao.selectOnlineByExpired(lastAccessTime); - } -} diff --git a/ruoyi-system/src/main/resources/mapper/system/SysUserOnlineMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysUserOnlineMapper.xml deleted file mode 100644 index ff85fd81..00000000 --- a/ruoyi-system/src/main/resources/mapper/system/SysUserOnlineMapper.xml +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - - - - - - - - - - - - - select sessionId, login_name, dept_name, ipaddr, login_location, browser, os, status, start_timestamp, last_access_time, expire_time - from sys_user_online - - - - - - replace into sys_user_online(sessionId, login_name, dept_name, ipaddr, login_location, browser, os, status, start_timestamp, last_access_time, expire_time) - values (#{sessionId}, #{loginName}, #{deptName}, #{ipaddr}, #{loginLocation}, #{browser}, #{os}, #{status}, #{startTimestamp}, #{lastAccessTime}, #{expireTime}) - - - - delete from sys_user_online where sessionId = #{sessionId} - - - - - - - \ No newline at end of file