• Shiro源代码分析之两种Session的方式


    1Shiro默认的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)两个方法。这两个各自是对ShiroremembereMe功能和Session功能的管理。当中在构造方法里面能够看到,事实上一开是就设置了SessionManager。就是默认的:ServletContainerSessionManager().接下来看看默认的ServletContainerSessionManager()是怎么玩转Session.看下图。这个图显示了这个类的这些个方法



    这个类通过getSession(SessionKey)获得Sesison,以下看看这种方法干了些什么(代码1-2

    (代码1-2

    publicSession getSession(SessionKey key) throws SessionException {

            if (!WebUtils.isHttp(key)) { //推断是不是httpkey,否则抛异常
                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是不是Httpkey。假设是就继续,不是就抛异常.key这个是带了ServletRequestServletResponseWebSessinKey,详细怎么new出来的,看DefaultWebSecurityManager的这方法代码就知道了(代码1-3。这里里,将RequestResponse还有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这种方法,这种方法就是将传入的HttpSessionhost传进去,封装成ShiroHttpServletSession (代码 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="3600000" />
            <property name="sessionDAO" ref="sessionDAO" />
        </bean>
        <bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO">
            <property name="activeSessionsCacheName" value="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的父类SessionsSecurityManagerSessionstart(SessionContext context)。以下是这种方法的代码(代码2-2

    (代码2-2

    public Session start(SessionContext context) throws AuthorizationException {
            return this.sessionManager.start(context);
        }



    然后调用sessionManagerstart方法来创建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 飞丶天

  • 相关阅读:
    HDU-3622 Bomb Game 2sat
    HDU-4115 Eliminate the Conflict 2sat
    POJ-3678 Katu Puzzle 2sat
    [转]2-SAT问题及其算法
    Ros学习——roslaunch
    Ros学习调试——rqt_console
    Ros学习——创建程序包
    Ros学习service——小海龟
    Ros学习topic——小海龟
    旋转矩阵相关变换
  • 原文地址:https://www.cnblogs.com/cynchanpin/p/7200968.html
Copyright © 2020-2023  润新知