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" alt="">

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

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

  1. 牛客网Java刷题知识点之Map的两种取值方式keySet和entrySet、HashMap 、Hashtable、TreeMap、LinkedHashMap、ConcurrentHashMap 、WeakHashMap

    不多说,直接上干货! 这篇我是从整体出发去写的. 牛客网Java刷题知识点之Java 集合框架的构成.集合框架中的迭代器Iterator.集合框架中的集合接口Collection(List和Set). ...

  2. 关于VAD的两种内存隐藏方式

    Windows内核分析索引目录:https://www.cnblogs.com/onetrainee/p/11675224.html 技术学习来源:火哥(QQ:471194425) 内存在0环的两种内 ...

  3. Windows Azure VM的两种shut down 方式

    今天在调查Azure的价格时,发现下面的语句,来自http://azure.microsoft.com/en-us/pricing/details/virtual-machines/ * If my ...

  4. 两种隐藏元素方式【display: none】和【visibility: hidden】的区别

    此随笔的灵感来源于上周的一个面试,在谈到隐藏元素的时候,面试官突然问我[display: none]和[visibility: hidden]的区别,我当时一愣,这俩有区别吗,好像有,但是忘记了啊,因 ...

  5. Linux共享库两种加载方式简述

      Linux共享库两种加载方式简述  动态库技术通常能减少程序的大小,节省空间,提高效率,具有很高的灵活性,对于升级软件版本也更加容易.与静态库不同,动态库里面的函数不是执行程序本身 的一部分,而是 ...

  6. android环境下两种md5加密方式

    在平时开发过程中,MD5加密是一个比较常用的算法,最常见的使用场景就是在帐号注册时,用户输入的密码经md5加密后,传输至服务器保存起来.虽然md5加密经常用,但是md5的加密原理我还真说不上来,对md ...

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

    Form提供了两种数据传输的方式——get和post.虽然它们都是数据的提交方式,但是在实际传输时确有很大的不同,并且可能会对数据产生严重的影响.虽然为了方便的得到变量值,Web容器已经屏蔽了二者的一 ...

  8. 两种数据传输的方式——get和post。

    Form提供了两种数据传输的方式——get和post.虽然它们都是数据的提交方式,但是在实际传输时确有很大的不同,并且可能会对数据产生严重的影响.虽然为了方便的得到变量值,Web容器已经屏蔽了二者的一 ...

  9. Xamarin Android Fragment的两种加载方式

    android Fragment的重点: 3.0版本后引入,即minSdk要大于11 Fragment需要嵌套在Activity中使用,当然也可以嵌套到另外一个Fragment中,但这个被嵌套的Fra ...

随机推荐

  1. mysql5.6.35源码安装记录

    mysql数据库源码安装: 源码地址:wget http://mirrors.163.com/mysql/Downloads/MySQL-5.6/mysql-5.6.xx.tar.gz #安装前准备, ...

  2. 【OS_Linux】Linux下软件的安装与卸载

    1.Linux中软件安装包的分类 1) 一类是可执行的软件包,无需编译直接安装.在Windows中所有的软件包都是这种类型.安装完这个程序后,你就可以使用,但你看不到源程序.而且下载时要注意这个软件是 ...

  3. redis集群理解

    Redis在3.0中也引入了集群的概念,用于解决一些大数据量和高可用的问题,但是,为了达到高性能的目的,集群不是强一致性的,使用的是异步复制,在数据到主节点后,主节点返回成功,数据被异步地复制给从节点 ...

  4. (23)zabbix单位符号Unit symbols

    概述 在zabbix里面,我们不需要使用大数字来,例如我们可以不使用86400来表示一天,这个数字又不容易理解也容易出错.用什么办法来解决大数字问题呢?我们可以使用单位来简化,例如简化zabbix触发 ...

  5. 《Spring源码深度解析》第二章 容器的基本实现

    入门级别的spring配置文件 <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi=&q ...

  6. laravel中对加载进行优化

    在laravel中的模型与模型之间创建好关联关系会比较方便的方法 但是我们为了方便,有时也会忽略一些东西,比如: 我们在控制器中把整个一个文章对象传到了模板页面 在一次for循环下, 我们对数据进行了 ...

  7. 某比赛小记2- 从HTTP请求返回中获得答案

    题目:在A页面登录后,重定向到A页面,然后访问B页面,header中带一指定内容"Content":"2018",然后从response中读取answer的值. ...

  8. PAT Basic 1061

    1061 判断题 判断题的评判很简单,本题就要求你写个简单的程序帮助老师判题并统计学生们判断题的得分. 输入格式: 输入在第一行给出两个不超过 100 的正整数 N 和 M,分别是学生人数和判断题数量 ...

  9. 数据库---大数据+hadoop

    大数据:hadoop:大数据和hadoop的关系

  10. wei UI使用

    1.前言 通过前面系列文章的学习与讲解,相信大家已经对微信的开发有了一个全新的认识.后端基本能够基于盛派的第三方sdk搞定大部分事宜,剩下的就是前端了.关于手机端的浏览器的兼容性问题相信一直是开发者们 ...