新增会话定时清理
This commit is contained in:
parent
623c70d6fc
commit
bfaf973e54
|
|
@ -268,8 +268,8 @@ create table sys_user_online (
|
|||
primary key (sessionId)
|
||||
) engine=innodb default charset=utf8;
|
||||
|
||||
insert into sys_user_online(sessionId, login_name, dept_name, login_name, ipaddr, browser, os, status)
|
||||
values('c3b252c3-2229-4be4-a5f7-7aba4b0c314c', 'admin', '开发部', '管理员', '127.0.0.1', 'Chrome 45', 'Windows 7');
|
||||
insert into sys_user_online(sessionId, login_name, dept_name, role_name, ipaddr, browser, os, status)
|
||||
values('c3b252c3-2229-4be4-a5f7-7aba4b0c314c', 'admin', '开发部', '管理员', '127.0.0.1', 'Chrome 45', 'Windows 7', 'on_line');
|
||||
|
||||
-- 用户部门表
|
||||
SELECT * FROM sys_dept;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
package com.ruoyi.common.tools;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
public class DateTools
|
||||
{
|
||||
|
||||
public static final String DEFAULT_YYYYMMDD = "yyyyMMddHHmmss";
|
||||
|
||||
public static final String DEFAULT_YYYY_MM_DD = "yyyy-MM-dd HH:mm:ss";
|
||||
|
||||
public static final String dateTimeStr()
|
||||
{
|
||||
return dateTimeNow(DEFAULT_YYYY_MM_DD);
|
||||
}
|
||||
|
||||
public static final String dateTimeNow()
|
||||
{
|
||||
return dateTimeNow(DEFAULT_YYYYMMDD);
|
||||
}
|
||||
|
||||
public static final String dateTimeNow(final String format)
|
||||
{
|
||||
return dateTime(format, new Date());
|
||||
}
|
||||
|
||||
public static final String dateTime(final Date date)
|
||||
{
|
||||
return dateTime(DEFAULT_YYYYMMDD, date);
|
||||
}
|
||||
|
||||
public static final String dateTime(final String format, final Date date)
|
||||
{
|
||||
return new SimpleDateFormat(format).format(date);
|
||||
}
|
||||
|
||||
public static final Date dateTime(final String format, final String ts)
|
||||
{
|
||||
try
|
||||
{
|
||||
return new SimpleDateFormat(format).parse(ts);
|
||||
}
|
||||
catch (ParseException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -23,6 +23,7 @@ import java.sql.SQLException;
|
|||
public class DruidDBConfig
|
||||
{
|
||||
private Logger logger = LoggerFactory.getLogger(DruidDBConfig.class);
|
||||
|
||||
@Value("${spring.datasource.url}")
|
||||
private String dbUrl;
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import java.util.Locale;
|
|||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.servlet.LocaleResolver;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
|
||||
|
|
@ -16,6 +17,7 @@ import org.springframework.web.servlet.i18n.SessionLocaleResolver;
|
|||
* @author yangzz
|
||||
*/
|
||||
@Configuration
|
||||
@Component
|
||||
public class I18nConfig extends WebMvcConfigurerAdapter
|
||||
{
|
||||
|
||||
|
|
|
|||
|
|
@ -2,29 +2,27 @@ package com.ruoyi.framework.config;
|
|||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
|
||||
import org.apache.shiro.cache.ehcache.EhCacheManager;
|
||||
import org.apache.shiro.mgt.SecurityManager;
|
||||
import org.apache.shiro.session.mgt.SessionFactory;
|
||||
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
|
||||
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
|
||||
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
|
||||
import org.apache.shiro.web.filter.authc.LogoutFilter;
|
||||
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
|
||||
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
|
||||
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import com.ruoyi.common.utils.spring.SpringUtils;
|
||||
import com.ruoyi.project.shiro.realm.UserRealm;
|
||||
import com.ruoyi.project.shiro.session.OnlineSessionDAO;
|
||||
import com.ruoyi.project.shiro.session.OnlineSessionFactory;
|
||||
import com.ruoyi.project.shiro.web.OnlineSessionFilter;
|
||||
import com.ruoyi.project.shiro.web.sync.SyncOnlineSessionFilter;
|
||||
import com.ruoyi.project.shiro.web.filter.online.OnlineSessionFilter;
|
||||
import com.ruoyi.project.shiro.web.filter.sync.SyncOnlineSessionFilter;
|
||||
import com.ruoyi.project.shiro.web.session.OnlineWebSessionManager;
|
||||
import com.ruoyi.project.shiro.web.session.SpringSessionValidationScheduler;
|
||||
import com.ruoyi.project.system.menu.service.MenuServiceImpl;
|
||||
|
||||
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
|
||||
|
|
@ -39,6 +37,18 @@ public class ShiroConfig
|
|||
{
|
||||
public static final String PREMISSION_STRING = "perms[\"{0}\"]";
|
||||
|
||||
// Session超时时间,单位为毫秒(默认30分钟)
|
||||
@Value("${shiro.session.expireTime}")
|
||||
private String expireTime;
|
||||
|
||||
// 同步session到数据库的周期 单位为毫秒(默认1分钟)
|
||||
@Value("${shiro.session.dbSyncPeriod}")
|
||||
private String dbSyncPeriod;
|
||||
|
||||
// 相隔多久检查一次session的有效性,单位毫秒,默认就是30分钟
|
||||
@Value("${shiro.session.validationInterval}")
|
||||
private String validationInterval;
|
||||
|
||||
/**
|
||||
* 缓存管理器 使用Ehcache实现
|
||||
*/
|
||||
|
|
@ -75,24 +85,68 @@ public class ShiroConfig
|
|||
* 自定义sessionFactory会话
|
||||
*/
|
||||
@Bean
|
||||
SessionFactory sessionFactory()
|
||||
OnlineSessionFactory sessionFactory()
|
||||
{
|
||||
OnlineSessionFactory sessionFactory = new OnlineSessionFactory();
|
||||
return sessionFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义sessionFactory调度器
|
||||
*/
|
||||
@Bean
|
||||
SpringSessionValidationScheduler sessionValidationScheduler()
|
||||
{
|
||||
SpringSessionValidationScheduler sessionValidationScheduler = new SpringSessionValidationScheduler();
|
||||
// 相隔多久检查一次session的有效性,单位毫秒,默认就是60分钟
|
||||
sessionValidationScheduler.setSessionValidationInterval(60 * 60 * 1000);
|
||||
// 设置会话验证调度器进行会话验证时的会话管理器
|
||||
sessionValidationScheduler.setSessionManager(sessionValidationManager());
|
||||
return sessionValidationScheduler;
|
||||
}
|
||||
|
||||
/**
|
||||
* 会话管理器
|
||||
*/
|
||||
@Bean
|
||||
public DefaultWebSessionManager configWebSessionManager()
|
||||
public OnlineWebSessionManager sessionValidationManager()
|
||||
{
|
||||
DefaultWebSessionManager manager = new DefaultWebSessionManager();
|
||||
manager.setCacheManager(getEhCacheManager());// 加入缓存管理器
|
||||
manager.setSessionDAO(sessionDAO());// 设置SessionDao
|
||||
manager.setDeleteInvalidSessions(true);// 删除过期的session
|
||||
manager.setGlobalSessionTimeout(sessionDAO().getExpireTime());// 设置全局session超时时间
|
||||
manager.setSessionValidationSchedulerEnabled(true);// 是否定时检查session
|
||||
OnlineWebSessionManager manager = new OnlineWebSessionManager();
|
||||
// 加入缓存管理器
|
||||
manager.setCacheManager(getEhCacheManager());
|
||||
// 删除过期的session
|
||||
manager.setDeleteInvalidSessions(true);
|
||||
// 设置全局session超时时间
|
||||
manager.setGlobalSessionTimeout(sessionDAO().getExpireTime());
|
||||
// 是否定时检查session
|
||||
manager.setSessionValidationSchedulerEnabled(true);
|
||||
// 自定义SessionDao
|
||||
manager.setSessionDAO(sessionDAO());
|
||||
// 自定义sessionFactory
|
||||
manager.setSessionFactory(sessionFactory());
|
||||
return manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* 会话管理器
|
||||
*/
|
||||
@Bean
|
||||
public OnlineWebSessionManager sessionManager()
|
||||
{
|
||||
OnlineWebSessionManager manager = new OnlineWebSessionManager();
|
||||
// 加入缓存管理器
|
||||
manager.setCacheManager(getEhCacheManager());
|
||||
// 删除过期的session
|
||||
manager.setDeleteInvalidSessions(true);
|
||||
// 设置全局session超时时间
|
||||
manager.setGlobalSessionTimeout(sessionDAO().getExpireTime());
|
||||
// 定义要使用的无效的Session定时调度器
|
||||
manager.setSessionValidationScheduler(sessionValidationScheduler());
|
||||
// 是否定时检查session
|
||||
manager.setSessionValidationSchedulerEnabled(true);
|
||||
// 自定义SessionDao
|
||||
manager.setSessionDAO(sessionDAO());
|
||||
// 自定义sessionFactory
|
||||
manager.setSessionFactory(sessionFactory());
|
||||
return manager;
|
||||
}
|
||||
|
|
@ -109,7 +163,7 @@ public class ShiroConfig
|
|||
// 注入缓存管理器;
|
||||
securityManager.setCacheManager(getEhCacheManager());
|
||||
// session管理器
|
||||
securityManager.setSessionManager(configWebSessionManager());
|
||||
securityManager.setSessionManager(sessionManager());
|
||||
return securityManager;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -47,8 +47,4 @@ public interface ShiroConstants
|
|||
*/
|
||||
public String ONLINE_SESSION = "online_session";
|
||||
|
||||
/**
|
||||
* 仅清空本地缓存 不情况数据库的
|
||||
*/
|
||||
public String ONLY_CLEAR_CACHE = "online_session_only_clear_cache";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,13 +14,15 @@ import com.ruoyi.project.util.HttpContextUtils;
|
|||
|
||||
/**
|
||||
* 针对自定义的ShiroSession的db操作
|
||||
*
|
||||
* @author yangzz
|
||||
*/
|
||||
public class OnlineSessionDAO extends EnterpriseCacheSessionDAO
|
||||
{
|
||||
/**
|
||||
* Session超时时间,单位为毫秒(默认3分钟)
|
||||
* Session超时时间,单位为毫秒(默认30分钟)
|
||||
*/
|
||||
private long expireTime = 3 * 60 * 1000;
|
||||
private long expireTime = 30 * 60 * 1000;
|
||||
|
||||
/**
|
||||
* 同步session到数据库的周期 单位为毫秒(默认1分钟)
|
||||
|
|
@ -58,14 +60,7 @@ public class OnlineSessionDAO extends EnterpriseCacheSessionDAO
|
|||
@Override
|
||||
protected Session doReadSession(Serializable sessionId)
|
||||
{
|
||||
HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
|
||||
String uri = request.getServletPath();
|
||||
// 如果是静态文件,则不更新SESSION
|
||||
if (checkStaticLink(uri))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
System.out.println("==============doReadSession url=================" + uri);
|
||||
System.out.println("==============doReadSession url=================");
|
||||
UserOnline userOnline = onlineService.selectByOnlineId(String.valueOf(sessionId));
|
||||
if (userOnline == null)
|
||||
{
|
||||
|
|
@ -156,6 +151,11 @@ public class OnlineSessionDAO extends EnterpriseCacheSessionDAO
|
|||
{
|
||||
linkFlag = true;
|
||||
}
|
||||
// 如果是登录请求,则不更新SESSION
|
||||
if (StringUtils.endsWithAny(uri, new String[] { "/" }))
|
||||
{
|
||||
linkFlag = true;
|
||||
}
|
||||
// 如果是静态文件,则不更新SESSION
|
||||
if (StringUtils.startsWith(uri, "/css") && StringUtils.endsWith(uri, ".css")
|
||||
|| StringUtils.startsWith(uri, "/js") && StringUtils.endsWith(uri, ".js")
|
||||
|
|
|
|||
|
|
@ -6,17 +6,29 @@ import org.apache.shiro.session.mgt.SessionContext;
|
|||
import org.apache.shiro.session.mgt.SessionFactory;
|
||||
import org.apache.shiro.web.session.mgt.WebSessionContext;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.ruoyi.common.tools.StringTools;
|
||||
import com.ruoyi.project.shiro.common.utils.IpUtils;
|
||||
import com.ruoyi.project.system.online.domain.OnlineSession;
|
||||
import com.ruoyi.project.system.online.domain.UserOnline;
|
||||
import com.ruoyi.project.util.HttpContextUtils;
|
||||
import eu.bitwalker.useragentutils.UserAgent;
|
||||
|
||||
/**
|
||||
* 自定义sessionFactory会话
|
||||
*
|
||||
* @author yangzz
|
||||
*/
|
||||
@Component
|
||||
public class OnlineSessionFactory implements SessionFactory
|
||||
{
|
||||
public Session createSession(UserOnline userOnline)
|
||||
{
|
||||
OnlineSession onlineSession = userOnline.getSession();
|
||||
if (StringTools.isNotNull(onlineSession) && onlineSession.getId() == null)
|
||||
{
|
||||
onlineSession.setId(userOnline.getSessionId());
|
||||
}
|
||||
return userOnline.getSession();
|
||||
}
|
||||
|
||||
|
|
@ -30,7 +42,8 @@ public class OnlineSessionFactory implements SessionFactory
|
|||
HttpServletRequest request = (HttpServletRequest) sessionContext.getServletRequest();
|
||||
if (request != null)
|
||||
{
|
||||
UserAgent userAgent = UserAgent.parseUserAgentString(HttpContextUtils.getHttpServletRequest().getHeader("User-Agent"));
|
||||
UserAgent userAgent = UserAgent
|
||||
.parseUserAgentString(HttpContextUtils.getHttpServletRequest().getHeader("User-Agent"));
|
||||
// 获取客户端操作系统
|
||||
String os = userAgent.getOperatingSystem().getName();
|
||||
// 获取客户端浏览器
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package com.ruoyi.project.shiro.web;
|
||||
package com.ruoyi.project.shiro.web.filter.online;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.servlet.ServletRequest;
|
||||
|
|
@ -14,6 +14,7 @@ import com.ruoyi.project.shiro.session.OnlineSessionDAO;
|
|||
import com.ruoyi.project.system.online.domain.OnlineSession;
|
||||
import com.ruoyi.project.system.user.domain.User;
|
||||
|
||||
|
||||
public class OnlineSessionFilter extends AccessControlFilter
|
||||
{
|
||||
|
||||
|
|
@ -86,6 +87,7 @@ public class OnlineSessionFilter extends AccessControlFilter
|
|||
}
|
||||
|
||||
// 跳转到登录页
|
||||
@Override
|
||||
protected void redirectToLogin(ServletRequest request, ServletResponse response) throws IOException
|
||||
{
|
||||
String loginUrl = getForceLogoutUrl();
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.ruoyi.project.shiro.web.sync;
|
||||
package com.ruoyi.project.shiro.web.filter.sync;
|
||||
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
|
|
@ -8,6 +8,11 @@ import com.ruoyi.project.shiro.ShiroConstants;
|
|||
import com.ruoyi.project.shiro.session.OnlineSessionDAO;
|
||||
import com.ruoyi.project.system.online.domain.OnlineSession;
|
||||
|
||||
/**
|
||||
* 同步Session数据到Db
|
||||
*
|
||||
* @author yangzz
|
||||
*/
|
||||
public class SyncOnlineSessionFilter extends PathMatchingFilter
|
||||
{
|
||||
@Autowired
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
package com.ruoyi.project.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.utils.spring.SpringUtils;
|
||||
import com.ruoyi.project.shiro.ShiroConstants;
|
||||
import com.ruoyi.project.system.online.domain.OnlineSession;
|
||||
import com.ruoyi.project.system.online.domain.UserOnline;
|
||||
import com.ruoyi.project.system.online.service.UserOnlineServiceImpl;
|
||||
|
||||
/**
|
||||
* 主要是在此如果会话的属性修改了 就标识下其修改了 然后方便 OnlineSessionDao同步
|
||||
*
|
||||
* @author yangzz
|
||||
*/
|
||||
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 s = (OnlineSession) doGetSession(sessionKey);
|
||||
s.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 = (OnlineSession) doGetSession(sessionKey);
|
||||
s.markAttributeChanged();
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证session是否有效 用于删除过期session
|
||||
*/
|
||||
@Override
|
||||
public void validateSessions()
|
||||
{
|
||||
if (log.isInfoEnabled())
|
||||
{
|
||||
log.info("invalidation sessions...");
|
||||
}
|
||||
|
||||
int invalidCount = 0;
|
||||
|
||||
int timeout = (int) this.getGlobalSessionTimeout();
|
||||
Date expiredDate = DateUtils.addMilliseconds(new Date(), 0 - timeout);
|
||||
UserOnlineServiceImpl userOnlineService = SpringUtils.getBean(UserOnlineServiceImpl.class);
|
||||
List<UserOnline> userOnlineList = userOnlineService.selectByOnlineExpired(expiredDate);
|
||||
// 批量过期删除
|
||||
List<String> needOfflineIdList = new ArrayList<String>();
|
||||
for (UserOnline 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());
|
||||
}
|
||||
|
||||
}
|
||||
if (needOfflineIdList.size() > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
userOnlineService.batchDeleteByOnline(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<Session> getActiveSessions()
|
||||
{
|
||||
throw new UnsupportedOperationException("getActiveSessions method not supported");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
package com.ruoyi.project.shiro.web.session;
|
||||
|
||||
import java.util.concurrent.Executors;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 自定义任务调度器完成
|
||||
*
|
||||
* @author yangzz
|
||||
*/
|
||||
public class SpringSessionValidationScheduler implements SessionValidationScheduler
|
||||
{
|
||||
|
||||
public static final long DEFAULT_SESSION_VALIDATION_INTERVAL = DefaultSessionManager.DEFAULT_SESSION_VALIDATION_INTERVAL;
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SpringSessionValidationScheduler.class);
|
||||
|
||||
/**
|
||||
* 定时器,用于处理超时的挂起请求,也用于连接断开时的重连。
|
||||
*/
|
||||
private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
|
||||
|
||||
private volatile boolean enabled = false;
|
||||
|
||||
/**
|
||||
* The session manager used to validate sessions.
|
||||
*/
|
||||
private ValidatingSessionManager sessionManager;
|
||||
|
||||
/**
|
||||
* The session validation interval in milliseconds.
|
||||
*/
|
||||
private long sessionValidationInterval = DEFAULT_SESSION_VALIDATION_INTERVAL;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public SpringSessionValidationScheduler()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor that specifies the session manager that should be used for validating sessions.
|
||||
*
|
||||
* @param sessionManager the <tt>SessionManager</tt> that should be used to validate sessions.
|
||||
*/
|
||||
public SpringSessionValidationScheduler(ValidatingSessionManager sessionManager)
|
||||
{
|
||||
this.sessionManager = sessionManager;
|
||||
}
|
||||
|
||||
public void setSessionManager(ValidatingSessionManager sessionManager)
|
||||
{
|
||||
this.sessionManager = sessionManager;
|
||||
}
|
||||
|
||||
@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.
|
||||
*
|
||||
* <p>
|
||||
* 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, 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...");
|
||||
}
|
||||
|
||||
this.enabled = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -22,6 +22,7 @@ public class LogininforServiceImpl implements ILogininforService
|
|||
*
|
||||
* @param logininfor 访问日志对象
|
||||
*/
|
||||
@Override
|
||||
public void insertLogininfor(Logininfor logininfor)
|
||||
{
|
||||
logininforDao.insertLogininfor(logininfor);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package com.ruoyi.project.system.online.dao;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.ruoyi.project.system.online.domain.UserOnline;
|
||||
|
||||
public interface IUserOnlineDao
|
||||
|
|
@ -35,4 +34,12 @@ public interface IUserOnlineDao
|
|||
* @param online 会话信息
|
||||
*/
|
||||
public List<UserOnline> selectUserOnlines();
|
||||
|
||||
|
||||
/**
|
||||
* 查询过期会话集合
|
||||
*
|
||||
* @param lastAccessTime 过期时间
|
||||
*/
|
||||
public List<UserOnline> selectByOnlineExpired(String lastAccessTime);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ public class UserOnlineDaoImpl extends DynamicObjectBaseDao implements IUserOnli
|
|||
* @param sessionId 会话ID
|
||||
* @return 在线用户信息
|
||||
*/
|
||||
@Override
|
||||
public int deleteByOnlineId(String sessionId)
|
||||
{
|
||||
return this.delete("SystemOnlineMapper.deleteByOnlineId", sessionId);
|
||||
|
|
@ -36,6 +37,7 @@ public class UserOnlineDaoImpl extends DynamicObjectBaseDao implements IUserOnli
|
|||
*
|
||||
* @param online 会话信息
|
||||
*/
|
||||
@Override
|
||||
public int saveByOnline(UserOnline online)
|
||||
{
|
||||
return this.save("SystemOnlineMapper.saveByOnline", online);
|
||||
|
|
@ -46,6 +48,7 @@ public class UserOnlineDaoImpl extends DynamicObjectBaseDao implements IUserOnli
|
|||
*
|
||||
* @param online 会话信息
|
||||
*/
|
||||
@Override
|
||||
public List<UserOnline> selectUserOnlines()
|
||||
{
|
||||
List<UserOnline> userOnlineList = null;
|
||||
|
|
@ -59,4 +62,24 @@ public class UserOnlineDaoImpl extends DynamicObjectBaseDao implements IUserOnli
|
|||
}
|
||||
return userOnlineList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询过期会话集合
|
||||
*
|
||||
* @param lastAccessTime 过期时间
|
||||
*/
|
||||
@Override
|
||||
public List<UserOnline> selectByOnlineExpired(String lastAccessTime)
|
||||
{
|
||||
List<UserOnline> userOnlineList = null;
|
||||
try
|
||||
{
|
||||
userOnlineList = this.findForList("SystemOnlineMapper.selectByOnlineExpired", lastAccessTime);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
return userOnlineList;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,11 +37,13 @@ public class OnlineSession extends SimpleSession
|
|||
// 属性是否改变 优化session数据同步
|
||||
private transient boolean attributeChanged = false;
|
||||
|
||||
@Override
|
||||
public String getHost()
|
||||
{
|
||||
return host;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHost(String host)
|
||||
{
|
||||
this.host = host;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.ruoyi.project.system.online.service;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import com.ruoyi.project.system.online.domain.UserOnline;
|
||||
|
||||
|
|
@ -21,6 +22,14 @@ public interface IUserOnlineService
|
|||
*/
|
||||
public void deleteByOnlineId(String sessionId);
|
||||
|
||||
/**
|
||||
* 通过会话序号删除信息
|
||||
*
|
||||
* @param sessions 会话ID集合
|
||||
* @return 在线用户信息
|
||||
*/
|
||||
public void batchDeleteByOnline(List<String> sessions);
|
||||
|
||||
/**
|
||||
* 保存会话信息
|
||||
*
|
||||
|
|
@ -41,4 +50,11 @@ public interface IUserOnlineService
|
|||
* @param sessionId 会话ID
|
||||
*/
|
||||
public void forceLogout(String sessionId);
|
||||
|
||||
/**
|
||||
* 查询会话集合
|
||||
*
|
||||
* @param online 会话信息
|
||||
*/
|
||||
public List<UserOnline> selectByOnlineExpired(Date expiredDate);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
package com.ruoyi.project.system.online.service;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.shiro.session.Session;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.ruoyi.common.tools.DateTools;
|
||||
import com.ruoyi.project.shiro.session.OnlineSessionDAO;
|
||||
import com.ruoyi.project.system.online.dao.IUserOnlineDao;
|
||||
import com.ruoyi.project.system.online.domain.UserOnline;
|
||||
|
|
@ -23,6 +27,7 @@ public class UserOnlineServiceImpl implements IUserOnlineService
|
|||
* @param sessionId 会话ID
|
||||
* @return 在线用户信息
|
||||
*/
|
||||
@Override
|
||||
public UserOnline selectByOnlineId(String sessionId)
|
||||
{
|
||||
return userOnlineDao.selectByOnlineId(sessionId);
|
||||
|
|
@ -34,6 +39,7 @@ public class UserOnlineServiceImpl implements IUserOnlineService
|
|||
* @param sessionId 会话ID
|
||||
* @return 在线用户信息
|
||||
*/
|
||||
@Override
|
||||
public void deleteByOnlineId(String sessionId)
|
||||
{
|
||||
UserOnline userOnline = selectByOnlineId(sessionId);
|
||||
|
|
@ -43,11 +49,31 @@ public class UserOnlineServiceImpl implements IUserOnlineService
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过会话序号删除信息
|
||||
*
|
||||
* @param sessions 会话ID集合
|
||||
* @return 在线用户信息
|
||||
*/
|
||||
@Override
|
||||
public void batchDeleteByOnline(List<String> sessions)
|
||||
{
|
||||
for (String sessionId : sessions)
|
||||
{
|
||||
UserOnline userOnline = selectByOnlineId(sessionId);
|
||||
if (userOnline != null)
|
||||
{
|
||||
userOnlineDao.deleteByOnlineId(sessionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存会话信息
|
||||
*
|
||||
* @param online 会话信息
|
||||
*/
|
||||
@Override
|
||||
public void saveByOnline(UserOnline online)
|
||||
{
|
||||
userOnlineDao.saveByOnline(online);
|
||||
|
|
@ -58,6 +84,7 @@ public class UserOnlineServiceImpl implements IUserOnlineService
|
|||
*
|
||||
* @param online 会话信息
|
||||
*/
|
||||
@Override
|
||||
public List<UserOnline> selectUserOnlines()
|
||||
{
|
||||
return userOnlineDao.selectUserOnlines();
|
||||
|
|
@ -68,6 +95,7 @@ public class UserOnlineServiceImpl implements IUserOnlineService
|
|||
*
|
||||
* @param sessionId 会话ID
|
||||
*/
|
||||
@Override
|
||||
public void forceLogout(String sessionId)
|
||||
{
|
||||
Session session = onlineSessionDAO.readSession(sessionId);
|
||||
|
|
@ -78,4 +106,16 @@ public class UserOnlineServiceImpl implements IUserOnlineService
|
|||
session.setTimeout(1000);
|
||||
userOnlineDao.deleteByOnlineId(sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询会话集合
|
||||
*
|
||||
* @param online 会话信息
|
||||
*/
|
||||
@Override
|
||||
public List<UserOnline> selectByOnlineExpired(Date expiredDate)
|
||||
{
|
||||
String lastAccessTime = DateTools.dateTime("yyyy-MM-dd HH:mm:ss", expiredDate);
|
||||
return userOnlineDao.selectByOnlineExpired(lastAccessTime);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
y-admin:
|
||||
ruoyi:
|
||||
uploadPath: D:/var/uploaded_files/
|
||||
logging:
|
||||
level:
|
||||
com.bootdo: debug
|
||||
com.ruoyi: debug
|
||||
spring:
|
||||
datasource:
|
||||
type: com.alibaba.druid.pool.DruidDataSource
|
||||
|
|
|
|||
|
|
@ -33,3 +33,12 @@ mybatis:
|
|||
mapperLocations: classpath:mybatis/**/*Mapper.xml
|
||||
# 加载全局的配置文件
|
||||
configLocation: classpath:mybatis/mybatis-config.xml
|
||||
# Shiro
|
||||
shiro:
|
||||
session:
|
||||
# Session超时时间(默认30分钟)
|
||||
expireTime: 30
|
||||
# 同步session到数据库的周期(默认1分钟)
|
||||
dbSyncPeriod: 1
|
||||
# 相隔多久检查一次session的有效性,默认就是60分钟
|
||||
validationInterval: 60
|
||||
|
|
@ -45,4 +45,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
select * from sys_user_online
|
||||
</select>
|
||||
|
||||
<select id="selectByOnlineExpired" parameterType="String" resultMap="UserOnlineResult">
|
||||
SELECT * FROM sys_user_online o
|
||||
WHERE o.last_access_time <![CDATA[ <= ]]> #{lastAccessTime} ORDER BY o.last_access_time ASC
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
Loading…
Reference in New Issue