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 3ae9e81e..fa3314f2 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 @@ -13,11 +13,11 @@ import com.ruoyi.common.annotation.Log; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.core.session.OnlineSession; 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.system.domain.SysUserOnline; import com.ruoyi.system.service.ISysUserOnlineService; diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/session/OnlineSession.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/session/OnlineSession.java similarity index 93% rename from ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/session/OnlineSession.java rename to ruoyi-common/src/main/java/com/ruoyi/common/core/session/OnlineSession.java index 95d9b4c7..86f38b6e 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/session/OnlineSession.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/session/OnlineSession.java @@ -1,4 +1,4 @@ -package com.ruoyi.framework.shiro.session; +package com.ruoyi.common.core.session; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; 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 e73af79e..ac0f8b18 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 @@ -1,9 +1,12 @@ package com.ruoyi.framework.manager.factory; +import java.io.ByteArrayOutputStream; +import java.io.ObjectOutputStream; import java.util.TimerTask; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.session.OnlineSession; import com.ruoyi.common.utils.AddressUtils; import com.ruoyi.common.utils.LogUtils; import com.ruoyi.common.utils.ServletUtils; @@ -11,7 +14,6 @@ import com.ruoyi.common.utils.ShiroUtils; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.http.UserAgentUtils; 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; @@ -54,8 +56,21 @@ public class AsyncFactory online.setBrowser(session.getBrowser()); online.setOs(session.getOs()); online.setStatus(session.getStatus()); + // 序列化 OnlineSession,重启后可从 DB 恢复会话 + try + { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(bos); + oos.writeObject(session); + oos.close(); + online.setSessionData(bos.toByteArray()); + } + catch (Exception e) + { + // 序列化失败不影响正常流程,仅记录日志 + LoggerFactory.getLogger(AsyncFactory.class).warn("serialize OnlineSession failed", e); + } SpringUtils.getBean(ISysUserOnlineService.class).saveOnline(online); - } }; } 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 index 1fb9c7e4..4d402250 100644 --- 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 @@ -4,9 +4,10 @@ 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.core.session.OnlineSession; import com.ruoyi.common.utils.StringUtils; -import com.ruoyi.framework.shiro.session.OnlineSession; import com.ruoyi.system.domain.SysUserOnline; +import com.ruoyi.framework.shiro.session.OnlineSessionFactory; import com.ruoyi.system.service.ISysUserOnlineService; /** @@ -20,6 +21,9 @@ public class SysShiroService @Autowired private ISysUserOnlineService onlineService; + @Autowired + private OnlineSessionFactory onlineSessionFactory; + /** * 删除会话 * @@ -44,19 +48,6 @@ public class SysShiroService 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; + return onlineSessionFactory.createSession(userOnline); } } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/session/OnlineSessionFactory.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/session/OnlineSessionFactory.java index 5043664d..6988ebf8 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/session/OnlineSessionFactory.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/shiro/session/OnlineSessionFactory.java @@ -1,13 +1,20 @@ package com.ruoyi.framework.shiro.session; +import java.io.ByteArrayInputStream; +import java.io.ObjectInputStream; import javax.servlet.http.HttpServletRequest; import org.apache.shiro.session.Session; import org.apache.shiro.session.mgt.SessionContext; import org.apache.shiro.session.mgt.SessionFactory; import org.apache.shiro.web.session.mgt.WebSessionContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; +import com.ruoyi.common.core.session.OnlineSession; import com.ruoyi.common.utils.IpUtils; +import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.http.UserAgentUtils; +import com.ruoyi.system.domain.SysUserOnline; /** * 自定义sessionFactory会话 @@ -17,6 +24,40 @@ import com.ruoyi.common.utils.http.UserAgentUtils; @Component public class OnlineSessionFactory implements SessionFactory { + private static final Logger log = LoggerFactory.getLogger(OnlineSessionFactory.class); + + public Session createSession(SysUserOnline userOnline) + { + // 优先从序列化数据恢复完整 Session(包含 principals 认证信息) + if (userOnline.getSessionData() != null && userOnline.getSessionData().length > 0) + { + try + { + ByteArrayInputStream bis = new ByteArrayInputStream(userOnline.getSessionData()); + ObjectInputStream ois = new ObjectInputStream(bis); + OnlineSession onlineSession = (OnlineSession) ois.readObject(); + ois.close(); + // 确保 sessionId 和 DB 一致 + if (onlineSession.getId() == null) + { + onlineSession.setId(userOnline.getSessionId()); + } + return onlineSession; + } + catch (Exception e) + { + log.warn("deserialize OnlineSession failed, sessionId={}", userOnline.getSessionId(), e); + } + } + // 降级:仅用基础字段重建(无认证信息,会触发重新登录) + OnlineSession onlineSession = userOnline.getSession(); + if (StringUtils.isNotNull(onlineSession) && onlineSession.getId() == null) + { + onlineSession.setId(userOnline.getSessionId()); + } + return onlineSession; + } + @Override public Session createSession(SessionContext initData) { 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 index 3f09f388..91c2fe44 100644 --- 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 @@ -10,9 +10,9 @@ 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.core.session.OnlineSession; 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; /** 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 index f8e51422..97099548 100644 --- 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 @@ -4,7 +4,7 @@ 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.common.core.session.OnlineSession; import com.ruoyi.framework.shiro.session.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 index 69582424..dc0e7a93 100644 --- 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 @@ -14,10 +14,10 @@ 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.core.session.OnlineSession; 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; diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserOnline.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserOnline.java index d259a5ba..64dd0a7b 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserOnline.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserOnline.java @@ -4,6 +4,7 @@ import java.util.Date; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import com.ruoyi.common.core.domain.BaseEntity; +import com.ruoyi.common.core.session.OnlineSession; import com.ruoyi.common.enums.OnlineStatus; /** @@ -48,6 +49,12 @@ public class SysUserOnline extends BaseEntity /** 在线状态 */ private OnlineStatus status = OnlineStatus.on_line; + /** 备份的当前用户会话 */ + private OnlineSession session; + + /** 序列化的Session数据\uff0c用于服务重吏后恢复会话 */ + private byte[] sessionData; + public String getSessionId() { return sessionId; @@ -157,8 +164,28 @@ public class SysUserOnline extends BaseEntity { this.status = status; } - - @Override + + public OnlineSession getSession() + { + return session; + } + + public void setSession(OnlineSession session) + { + this.session = session; + } + + public byte[] getSessionData() + { + return sessionData; + } + + public void setSessionData(byte[] sessionData) + { + this.sessionData = sessionData; + } + + @Override public String toString() { return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) .append("sessionId", getSessionId()) diff --git a/ruoyi-system/src/main/resources/mapper/system/SysUserOnlineMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysUserOnlineMapper.xml index 92f68435..e1e987c2 100644 --- a/ruoyi-system/src/main/resources/mapper/system/SysUserOnlineMapper.xml +++ b/ruoyi-system/src/main/resources/mapper/system/SysUserOnlineMapper.xml @@ -16,10 +16,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + - + - select sessionId, login_name, dept_name, ipaddr, login_location, browser, os, status, start_timestamp, last_access_time, expire_time + select sessionId, login_name, dept_name, ipaddr, login_location, browser, os, status, start_timestamp, last_access_time, expire_time, session_data from sys_user_online @@ -29,8 +30,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - 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}) + replace into sys_user_online(sessionId, login_name, dept_name, ipaddr, login_location, browser, os, status, start_timestamp, last_access_time, expire_time, session_data) + values (#{sessionId}, #{loginName}, #{deptName}, #{ipaddr}, #{loginLocation}, #{browser}, #{os}, #{status}, #{startTimestamp}, #{lastAccessTime}, #{expireTime}, #{sessionData,jdbcType=BLOB,typeHandler=org.apache.ibatis.type.BlobTypeHandler}) diff --git a/sql/ry_20260318.sql b/sql/ry_20260319.sql similarity index 98% rename from sql/ry_20260318.sql rename to sql/ry_20260319.sql index 26ff49ab..e326cfb4 100644 --- a/sql/ry_20260318.sql +++ b/sql/ry_20260319.sql @@ -588,6 +588,7 @@ create table sys_user_online ( start_timestamp datetime comment 'session创建时间', last_access_time datetime comment 'session最后访问时间', expire_time int(5) default 0 comment '超时时间,单位为分钟', + session_data blob default null comment '序列化的Session数据,用于服务重启后恢复会话', primary key (sessionId) ) engine=innodb comment = '在线用户记录'; @@ -664,7 +665,21 @@ insert into sys_notice values('3', '若依开源框架介绍', '1', '