目录
shiro的SessionManager
在官方架构图里面:
在默认的SecurityManager的uml图里面:
前面的CacheSecurityManager,realmSecurityManager,认证管理器,鉴权管理器都讲过了,那么这个Session管理器是怎么回事呢?
在web容器里面的session跟HttpSession是同一个吗?
首先看,我们在没有设置Session管理器的时候,默认使用的是哪一个?
先看SessionManager的具体实现:
第一个类是AbstractNativeSessionManager,是个抽象类,主要实现就是DefaultSessionManager跟DefaultWebSessionManager,而DefaultWebSessionManager又是继承了DefaultSessionManager,我们看一下DefaultWebSessionManager的uml图:
WebSessionManager里面就一个方法isServletContainerSessions判断是否是本地会话,就是相当于判断是不是httpSession.
再看SessionsSecurityManager,直接看我们使用的webSecurityManager:
ServletContainerSessionManager就相当于是使用HttpSession:ServletContainerSessionManager的createSession方法返回的是HttpServletSession,而HttpServletSession内部是直接维护了HttpSession对象,然后一系列方法是直接使用HttpSession进行操作的:
自定义sessionManager
看看ServletContainerSessionManager和DefaultWebSessionManager一起的uml图:
默认的是使用的ServletContainerSessionManager,如果我们想自定义,就直接使用DefaultWebSessionManager即可了;DefaultWebSessionManager需要设置的属性主要有:
- SessionListener,session监听
- setSessionDAO设置sessionDao,session缓存,或者持久化
- sessionIdCookie设置cookie
- setGlobalSessionTimeout全局会话超时时间
- setDeleteInvalidSessions是否删除无效session,默认true
- setSessionValidationSchedulerEnabled定时删除,默认true
- setSessionValidationInterval设置多长时间检测一下是否有过期session.然后删除
- setSessionIdUrlRewritingEnabled是否开启url后面加上sessionId
我们先看SessionDAO
sessionDao就是一系列的操作session的接口:
看实现:
抽象的就不看了,就三个具体实现:EnterpriseCacheSessionDAO,MemorySessionDAO,RedisSessionDAO,RedisSessionDAO就不说了比较简单,前面说shiro使用redis缓存的时候也说过了,这个那就是session缓存在redis里面,MemorySessionDAO也是,内部维护了一个ConcurrentMap来保存session,就是内存缓存了;EnterpriseCacheSessionDAO就是可以自己传一个CacheManager,好比ehcache的manager,然后就将缓存存在ehcache也可以直接传个内存缓存,也就放在内存里面了,也可以自定义sessionid的生成器,默认是JavaUuidSessionIdGenerator也就是uuid,可以自己实现.
使用内存缓存
直接使用MemorySessionDAO:
@Bean
public SessionDAO sessionDAO() {
return new MemorySessionDAO();
}
使用ehcache
// ehcache
public SessionDAO sessionDAO() {
EnterpriseCacheSessionDAO enterpriseCacheSessionDAO = new EnterpriseCacheSessionDAO();
EhCacheManager ehCacheManager = new EhCacheManager();
ehCacheManager.setCacheManager(getEhCacheManager());
enterpriseCacheSessionDAO.setCacheManager(ehCacheManager);
enterpriseCacheSessionDAO.setActiveSessionsCacheName("shiro-activeSessionCache");
enterpriseCacheSessionDAO.setSessionIdGenerator(new JavaUuidSessionIdGenerator());
return enterpriseCacheSessionDAO;
}
使用redis
org.crazycake.shiro.RedisSessionDAO的包注意:
public SessionDAO sessionDAO() {
//redis
RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
RedisManager redisManager = new RedisManager();
redisSessionDAO.setRedisManager(redisManager);
return redisSessionDAO;
}
剩下的配置比较简单:
/**
* 配置会话管理器,设定会话超时及保存
* @return
*/
@Bean
public SessionManager sessionManager() {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
Collection<SessionListener> listeners = new ArrayList<SessionListener>();
//配置监听
listeners.add(new ShiroSessionListener());
sessionManager.setSessionListeners(listeners);
sessionManager.setSessionIdCookie(sessionIdCookie());
sessionManager.setSessionDAO(sessionDAO());
sessionManager.setCacheManager(new MemoryConstrainedCacheManager());
sessionManager.setGlobalSessionTimeout(10000);
sessionManager.setDeleteInvalidSessions(true);
sessionManager.setSessionValidationSchedulerEnabled(true);
sessionManager.setSessionValidationInterval(5000);
sessionManager.setSessionIdUrlRewritingEnabled(false);
return sessionManager;
}
@Bean("sessionIdCookie")
public SimpleCookie sessionIdCookie(){
SimpleCookie simpleCookie = new SimpleCookie("sid");
simpleCookie.setHttpOnly(true);
simpleCookie.setPath("/");
//maxAge=-1表示浏览器关闭时失效此Cookie
simpleCookie.setMaxAge(-1);
return simpleCookie;
}
需要自己实现session监听:
public interface SessionListener {
void onStart(Session var1);
void onStop(Session var1);
void onExpiration(Session var1);
}
记录人数的:
public class ShiroSessionListener implements SessionListener {
/**
* 统计在线人数
* juc包下线程安全自增
*/
private final AtomicInteger sessionCount = new AtomicInteger(0);
/**
* 会话创建时触发
* @param session
*/
@Override
public void onStart(Session session) {
//会话创建,在线人数加一
sessionCount.incrementAndGet();
}
/**
* 退出会话时触发
* @param session
*/
@Override
public void onStop(Session session) {
//会话退出,在线人数减一
sessionCount.decrementAndGet();
}
/**
* 会话过期时触发
* @param session
*/
@Override
public void onExpiration(Session session) {
//会话过期,在线人数减一
sessionCount.decrementAndGet();
}
/**
* 获取在线人数使用
* @return
*/
public AtomicInteger getSessionCount() {
return sessionCount;
}
}
总结
这里是web的session简单用法,如果是java环境,那么session就比较简单了,前面都有分析到.不过现在一般都不使用session了,所以这一块,只需要知道就好了,现在项目一般都是有app端的,集群的,多应用共享的,就是单点登录这样的.所以基于传统的session已经很难实现需求了,解决方案一般是shiro+jwt基于token方式实现.后期会讲到