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的.看下图。这个图显示了这个类的这些个方法

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" >

这个类通过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="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的父类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
飞丶天

时间: 2024-08-04 05:43:38

Shiro源代码分析之两种Session的方式的相关文章

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

1.Shiro默认的Session处理方式 <!-- 定义 Shiro 主要业务对象 --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <!-- <property name="sessionManager" ref="sessionManager" />

Linux驱动的两种加载方式过程分析

一.概念简述 在Linux下可以通过两种方式加载驱动程序:静态加载和动态加载. 静态加载就是把驱动程序直接编译进内核,系统启动后可以直接调用.静态加载的缺点是调试起来比较麻烦,每次修改一个地方都要重新编译和下载内核,效率较低.若采用静态加载的驱动较多,会导致内核容量很大,浪费存储空间. 动态加载利用了Linux的module特性,可以在系统启动后用insmod命令添加模块(.ko),在不需要的时候用rmmod命令卸载模块,采用这种动态加载的方式便于驱动程序的调试,同时可以针对产品的功能需求,进行

Hibernate两种获得sessionFactory方式的区别

Hibernate 可以通过sessionFactory.openSession()和sessionFactory.getCurrentSession()来获得session,下面来浅说一下两者区别. [sessionFactory.openSession()]     通过session工厂sessionFactory.openSession()方法来获得一个session,每次都创建一个新的session出来,并且每次Hibernate都会打开数据库和应用程序的连接. [sessionFac

UIImage的两种加载方式

UIImage的两种加载方式 1.有缓存:读取后放入缓存中下次可直接读取,适用于图片较少且频繁使用. [UIImage imageNamed:@"文件名"]: 在缓存中由系统管理,当收到memoryWarning时会释放这些内存资源. 2.无缓存:用完就释放掉,参数传的是全路径,适用于图片较多较大的情况下. NSString *path = [[NSBundlemainBundle] pathForResource: @"1.png"ofType: nil]; [U

android环境下两种md5加密方式

在平时开发过程中,MD5加密是一个比较常用的算法,最常见的使用场景就是在帐号注册时,用户输入的密码经md5加密后,传输至服务器保存起来.虽然md5加密经常用,但是md5的加密原理我还真说不上来,对md5的认知目前仅仅停留在会使用的水平,想搞清楚还是要花点时间的,这是md5加密算法的相关介绍.本文主要介绍android平台下两种md5加密方式,分别为基于java语言的md5加密及ndk环境下基于c语言的md5加密. 下面代码为基于java语言的md5加密: public String getMD5

Form表单中method=post/get两种数据传输的方式的区别

Form提供了两种数据传输的方式—get和post,Form中的get和post方法,在数据传输过程中分别对应了HTTP协议中的GET和POST方法,感兴趣的朋友可以了解下,或许对你学习get/post有所帮助 Form提供了两种数据传输的方式——get和post.虽然它们都是数据的提交方式,但是在实际传输时确有很大的不同,并且可能会对数据产生严重的影响.虽然为了方便的得到变量值,Web容器已经屏蔽了二者的一些差异,但是了解二者的差异在以后的编程也会很有帮助的. Form中的get和post方法

python实现快速排序算法(两种不同实现方式)

# -*- coding: utf-8 -*- """ Created on Fri May 16 17:24:05 2014 @author: lifeix """ #快速排序 import sys import random length = 30 def qsort(arr,left,right): lp = left rp = right if lp == rp:return while True: while arr[lp] >=

(总结)Nginx使用的php-fpm的两种进程管理方式及优化

PS:前段时间配置php-fpm的时候,无意中发现原来它还有两种进程管理方式.与Apache类似,它的进程数也是可以根据设置分为动态和静态的. php-fpm目前主要又两个分支,分别对应于php-5.2.x的版本和php-5.3.x的版本.在5.2.x的版本中,php-fpm.conf使用的是xml格式,而在新的5.3.x版本中,则是和php.ini一样的配置风格. 在5.2.x版本中,php-fpm.conf中对于进程管理号称是有两种风格,一种是静态(static)的,一种是类似于apache

Windows Azure VM的两种shut down 方式

今天在调查Azure的价格时,发现下面的语句,来自http://azure.microsoft.com/en-us/pricing/details/virtual-machines/ * If my deployed instance is in the "stopped" state, do I still get billed? If your instance is in the Stopped Deallocated state, it is not billed. If yo