首页 > 代码库 > Shiro源代码分析之两种Session的方式
Shiro源代码分析之两种Session的方式
1、Shiro默认的Session处理方式
<!-- 定义 Shiro 主要业务对象 --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <!-- <property name="sessionManager" ref="sessionManager" /> --> <property name="realm" ref="systemAuthorizingRealm" /> <property name="cacheManager" ref="shiroCacheManager" /> </bean> |
这里从DefaultWebSecurityManager这里看起。这个代码是定义的Shiro安全管理对象,看以下的构造方法(代码1-1)
(代码 1-1) public DefaultWebSecurityManager() { super(); ((DefaultSubjectDAO) this.subjectDAO).setSessionStorageEvaluator(new DefaultWebSessionStorageEvaluator()); this.sessionMode = HTTP_SESSION_MODE; setSubjectFactory(new DefaultWebSubjectFactory()); setRememberMeManager(new CookieRememberMeManager()); setSessionManager(new ServletContainerSessionManager()); } |
从 构造方法里面能够看出,这里面有 publicvoid setRememberMeManager(RememberMeManager rememberMeManager)和 publicvoid setSessionManager(SessionManager sessionManager)两个方法。这两个各自是对Shiro的remembereMe功能和Session功能的管理。当中在构造方法里面能够看到,事实上一开是就设置了SessionManager。就是默认的:ServletContainerSessionManager().接下来看看默认的ServletContainerSessionManager()是怎么玩转Session的.看下图。这个图显示了这个类的这些个方法
这个类通过getSession(SessionKey)获得Sesison,以下看看这种方法干了些什么(代码1-2)
(代码1-2) publicSession getSession(SessionKey key) throws SessionException { if (!WebUtils.isHttp(key)) { //推断是不是http的key,否则抛异常 String msg = "SessionKey must be an HTTP compatible implementation."; throw new IllegalArgumentException(msg); } HttpServletRequest request = WebUtils.getHttpRequest(key); //通过工具类获得HttpServletRequest 这类是javax.servlet.http.HttpServletRequest; Session session = null; HttpSession httpSession = request.getSession(false);//先从request里获得本来存在的 if (httpSession != null) { session = createSession(httpSession, request.getRemoteHost());//假设不为空,就创建一个封装了的,为空就无论它 } return session; } |
能够看看凝视,凝视上都写好了。这里的意思是。首先推断封装好的Key是不是Http的key。假设是就继续,不是就抛异常.key这个是带了ServletRequest和ServletResponse的WebSessinKey,详细怎么new出来的,看DefaultWebSecurityManager的这方法代码就知道了(代码1-3)。这里里,将Request和Response还有sessionId传递进去
(代码1-3) @Override protected SessionKey getSessionKey(SubjectContext context) { if (WebUtils.isWeb(context)) { Serializable sessionId = context.getSessionId(); ServletRequest request = WebUtils.getRequest(context); ServletResponse response = WebUtils.getResponse(context); return new WebSessionKey(sessionId, request, response); } else { return super.getSessionKey(context); } } |
由于继承了 org.apache.shiro.session.mgt.DefaultSessionKey ,这个类是实现了SessionKey这个接口。 看看createSession这种方法,这种方法就是将传入的HttpSession和host传进去,封装成Shiro的HttpServletSession (代码 1-4)
(代码1-4) protectedSession createSession(HttpSession httpSession, String host) { return new HttpServletSession(httpSession, host); } |
关于默认的Session管理器,最后还看一下HttpServletSession这个类。就看一下的几个方法。还有些方法是没有放出来的,能够明显的看出。Shiro使用了一个叫Session的接口。但这个接口是和HttpSession的接口一模一样。就是通过HttpSession这个接口获得Session的功能(代码1-5)
(代码1-5) public class HttpServletSession implements Session { private HttpSession httpSession = null; public HttpServletSession(HttpSession httpSession, String host) { if (httpSession == null) { String msg = "HttpSession constructor argument cannot be null."; throw new IllegalArgumentException(msg); } if (httpSession instanceof ShiroHttpSession) { String msg = "HttpSession constructor argument cannot be an instance of ShiroHttpSession. This " + "is enforced to prevent circular dependencies and infinite loops."; throw new IllegalArgumentException(msg); } this.httpSession = httpSession; if (StringUtils.hasText(host)) { setHost(host); } } …………………… …………………… ……………… public Object getAttribute(Object key) throws InvalidSessionException { try { return httpSession.getAttribute(assertString(key)); } catch (Exception e) { throw new InvalidSessionException(e); } } public void setAttribute(Object key, Object value) throws InvalidSessionException { try { httpSession.setAttribute(assertString(key), value); } catch (Exception e) { throw new InvalidSessionException(e); } } ……………… ……………… } |
默认的模式就描写叙述到这里
2、接下来说另外一种方式。就是废弃掉tomcat自己的Session,使用企业级Session方案。这样的方案能够和容器无关。但在我们项目没有使用,由于用了开源连接池druid貌似logout的时候有点不正确。
<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager"> <property name="globalSessionTimeout" value=http://www.mamicode.com/"3600000" /> <property name="sessionDAO" ref="sessionDAO" /> </bean> <bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO"> <property name="activeSessionsCacheName" value=http://www.mamicode.com/"shiro-activeSessionCache" /> <property name="cacheManager" ref="shiroCacheManager" /> </bean> <!-- 用户授权信息Cache, 採用EhCache --> <bean id="shiroCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> <property name="cacheManager" ref="cacheManager"/> </bean>
必须向Spring注冊EnterpriseCacheSessionDAO然后将cacheManager注入进去
这样的配置的创建入口在SecurityUtils.getSubject().getSession();这里看以下代码(代码2-1)
(代码2-1) publicSession getSession(boolean create) { if (log.isTraceEnabled()) { log.trace("attempting to get session; create = " + create + "; session is null = " + (this.session == null) + "; session has id = " + (this.session != null && session.getId() != null)); } if (this.session == null && create) {//session为空,而且能创建 …… ……省略 …… SessionContext sessionContext = createSessionContext(); Session session = this.securityManager.start(sessionContext);//在这里创建Session this.session = decorate(session);//包装Session,他自己建的自己也去包装一下 } |
调用DefaultSecurityManager的父类SessionsSecurityManager的Sessionstart(SessionContext context)。以下是这种方法的代码(代码2-2)
(代码2-2) public Session start(SessionContext context) throws AuthorizationException { return this.sessionManager.start(context); } |
然后调用sessionManager的start方法来创建Session。创建Session的入口。就在这里。看以下代码分析(代码2-3)
(代码2-3) publicSession start(SessionContext context) { Session session = createSession(context);//创建Session applyGlobalSessionTimeout(session); onStart(session, context); notifyStart(session); return createExposedSession(session, context); } |
创建Session这里是调用?DefaultSessionManager的父类的createSession,事实上父类也没有真正来创建Session。
这里用到了模板方法。父类里面的doCreateSession是抽象方法,最后真正创建子类的还是交给子类去实现(代码2-4)
(代码2-4) protectedSession createSession(SessionContext context) throwsAuthorizationException { enableSessionValidationIfNecessary(); return doCreateSession(context); } protected abstract Session doCreateSession(SessionContext initData) throws AuthorizationException; |
?其它的也没多少可分析的。这里再看一下manager里面的sessionFacotry工厂的createSession方法(代码2-5)
(代码2-5) publicSession createSession(SessionContext initData) { if (initData != null) { String host = initData.getHost(); if (host != null) { return new SimpleSession(host); } } return new SimpleSession(); } |
这里的SimpleSession是实现了Session接口的。详细能够看看相关的类继承图
另外Session是怎么缓存进入Cache的呢?在以下的调用以下代码创建Session的过程中,以下方法会调用,而缓存就在create(s)这里面(代码2-6)
(代码2-6) protected Session doCreateSession(SessionContext context) { Session s = newSessionInstance(context); if (log.isTraceEnabled()) { log.trace("Creating session for host {}", s.getHost()); } create(s); return s; } |
经过一些步骤之后在CachingSessionDao里被缓存,以下是代码。
能够看以下的凝视(代码2-7)
(代码2-7) protectedvoid cache(Session session, Serializable sessionId) { if (session == null || sessionId == null) { return; } Cache<Serializable, Session> cache = getActiveSessionsCacheLazy();//获取缓存 if (cache == null) { return; } cache(session, sessionId, cache);//有缓存就存起来 } |
以上是Session的创建过程,获取Session就简单说吧。有些过程自己发现更有趣。这里会调用DefaultWebSessionManager的父类的getAttribute这种方法(代码2-8)
(代码2-8) publicObject getAttribute(SessionKey sessionKey, Object attributeKey)throws InvalidSessionException { return lookupRequiredSession(sessionKey).getAttribute(attributeKey); } |
?最后会调用CachingSessionDao的这个publicSession readSession(Serializable sessionId) throwsUnknownSessionException 在这里就会从缓存里读取Session(代码2-9)
(代码2-9) public Session readSession(Serializable sessionId) throws UnknownSessionException { Session s = getCachedSession(sessionId); if (s == null) { s = super.readSession(sessionId); } return s; } |
?这是getCachedSession(sessionId)的代码。看了代码想必非常easy理解了吧(代码2-10)
protected Session getCachedSession(Serializable sessionId) { Session cached = null; if (sessionId != null) { Cache<Serializable, Session> cache = getActiveSessionsCacheLazy(); if (cache != null) { cached = getCachedSession(sessionId, cache); } } return cached; } |
?获得了Session,要获得里面的值和对象就非常easy了
有问题欢迎提出来。由于是先写在编辑器上的。然后在复制到word上。所以代码是一致的黑色,希望可以讲究着看,写个原创文章不easy,眼睛都看肿了。所以转载的时候能带上作者,谢谢
作者:肖华
blog.csdn.net/xh199110飞丶天
Shiro源代码分析之两种Session的方式