5.会话管理
SessionManager用于管理Shiro中的Session信息。Session也就是我们通常说的会话,会话是用户在使用应用程序一段时间内携带的数据。传统的会话一般是基于Web容器(如:Tomcat、EJB环境等)。Shiro提供的Session可以在任何环境中使用,不再依赖于其他容器。
Session有一些实现类,包括SimpleSession、HttpServletSession和DelegatingSession等。SimpleSession是Shiro提供的一种简单实现,HttpServletSession是基于Sevlet中Session来实现的,DelegatingSession是一种委托机制,委托给SessionManager来实现。
下面是有关会话和过滤器的类关系图:
SessionManager分析
SessionManager管理着Session的创建、操作以及清除等。SessionManager有一些子接口,包括NativeSessionManager、ValidatingSessionManager和WebSessionManager。每个接口都提供了相关的抽象类AbstractSessionManager、AbstractNativeSessionManager、AbstractValidatingSessionManager。
SessionManager主要负责创建Session和获取Session,NativeSessionManager接口中包含了所有对Session的操作,这些操作方法和Session接口中是一致的,而ValidatingSessionManager接口提供了对Session校验的支持。
AbstractNativeSessionManager分析
该类负责管理Session的操作,但操作的具体实现是由Session自己实现的。相当于对Session操作前后做代理。
AbstractValidatingSessionManager分析
AbstractValidatingSessionManager的作用是定期的校验所有有效的Session状态,因为Session可能被停止或过期。该类继承了上面分析的AbstractNativeSessionManager,然后实现了ValidatingSessionManager接口中的validateSessions()方法。此类负责校验Session。另外此类还实现了Destroyable接口,表示销毁时应该处理销毁功能。该类会启动调度作业来校验Session,而调度作业的真正内容是检测每个Session的validate()方法。
5.1会话创建流程
具体流程如下:
1.发起请求时,首先会来到代理类DelegatingFilterProxy.doFilter方法中
先判断filter有没有初始化,没有初始化就现在初始化。然后调用正在的doFilter方法。也就是OncePerRequestFilter.doFilter
2.因为ShiroFilterFactoryBean 实现了FactoryBean,在springbean 进行依赖注入时getBean,此时会调用ShiroFilterFactoryBean.getObject的方法。
返回真正的过滤类为:SpringShiroFilter
3.SpringShiroFilter实现了OncePerRequestFilter,那么调用OncePerRequestFilter.doFilter也就是再执行我们的shiro过滤器了。
4.进入AbstractShiroFilter.doFilterInternal,重点createSubject。
5.跟下去进入:DefaultSecurityManager.createSubject
6.进入resolveSession(context)方法
7.进入resolveContextSession(context)方法
8.继续跟进
以上就完成了Session的获取。
9.接着走context = resolvePrincipals(context),获取你有没有登录的Principal信息。显然null。登录认证AuthenticationInfo也null,session也null。
10.开始进行拦截处理了。
executeChain这个方法是处理的方法。
我们先获取处理的类FilterChain。就是通过请求路径来取对应的FilterChain。当然这个我们也可以自定义。
11.依次会走ProxiedFIlterChain.doFilter OncePerRequestFilter.doFIlter AdviceFilter.doFilterInternal
进入AdviceFilter.doFilterInternal方法执行preHandle(request,response)
通过登录的链接拦截到后,执行对应过滤器的过滤方法。InvalidRequestFilter中的 isAccessAllowed。
13.如果返回结果为false则执行onAccessDenied方法
13。继续执行 executeChain(request, response, chain)方法。
14.同上,依次通过ProxiedFIlterChain.doFilter OncePerRequestFilter.doFIlter AdviceFilter.doFilterInternal
进入AdviceFilter.doFilterInternal方法执行preHandle(request,response),经过PathMatchingFilter进入AccessControlFilter中的onPreHandle方法
15.进入到AuthenticatingFilter的isAccessAllowed方法
16.如果isAccessAllowed方法为false进入到AuthenticatingFilter的onAccessDenied
17.进入saveRequestAndRedirectToLogin方法
18.进入AccessControlFilter的saveRequest方法
19.进入WebUtils中
20.进入getSession方法中
继续跟进
进入AbstractNativeSessionManager
进入AbstractValidatingSessionManager的createSession方法
接着进入DefaultSessionManager中的createSession方法
调用DefaultSessionManager的create方法
调用AbstractSessionDAO的create方法
执行doCreate方法,进入RedisSessionDao中的doCreate方法
其中saveSession方法是真正的存储操作
进行session的赋值和更新
此时session的保存就已经完成了。
5.2会话创建流程(第一次请求为登录时)
session的分析切入点是在:DefaultSecurityManger的login方法中。
继续DefaultSecurityManger
DefaultSecurityManger的createSubject方法
转移到DefaultSubjectDAO调用DefaultSubjectDAO.save(subject)方法
调用DefaultSubjectDAO.saveToSession(subject)方法
调用DefaultSubjectDAO的mergePrincipals方法
转移到DelegatingSubject调用DelegatingSubject的getSession
进入getSession方法中
继续跟进
进入AbstractNativeSessionManager
进入AbstractValidatingSessionManager的createSession方法
接着进入DefaultSessionManager中的createSession方法
调用DefaultSessionManager的create方法
SessionDao
调用AbstractSessionDAO的create方法
执行doCreate方法,进入RedisSessionDao中的doCreate方法
其中saveSession方法是真正的存储操作
回到DefaultSubjectDAO中,进行session属性的填充和更新缓存中的session。
跟进去进入到了Session的实现类ProxiedSession的session.setAttribute方法中
继续跟进,进入到了Session的另一个实现类DelegatingSession的setAttribute方法中
回到了SessionManager的子类AbstractNativeSessionManager的setAttribute方法中
进入onChange方法
再次回到DefaultSubjectDAO中执行mergeAuthenticatuonState方法
进入该方法
此时,session的创建、赋值和存储已完成。
5.2会话的刷新和过期校验。
SimpleSession是shiro完完全全的自己实现,是shiro对session的一种拓展;实现了ValidatingSession接口,具有自我校验的功能;一般不对外暴露,暴露的往往是他的代理。
1.刷新
ShiroFilter的类图如下:
流程如下:
1.进入AbstractShiroFilter中的doFilterInternal中
2.执行updateSessionLastAccessTime方法
此时session的刷新已经完成。
2.过期
之所以进行session校验是因为在每次发起请求后经过AbstractShiroFilter.doFilterInternal方法时,会通过Cookie中所携带的sessionId来进行session获取。如果session过期了,会重定向到登录页面,以此来进行会话的管理。sessionId是在第一次请求成功之后保存在客户端的。
启动校验定时任务
在AbstractValidatingSessionManager中createSession方法在调用doCreateSession方法之前调用enableSessionValidationIfNecessary()方法,代码如下
定时任务默认是每60分钟执行一次,第一次执行是在定时器初始化完成60分钟后执行。
在定时任务间隔期间,session的操作通过代理之后,都会来到sessionManager,sessionManager通过处理之后再到SimpleSession;AbstractNativeSessionManager中将session操作放给SimpleSession之前,都会调用lookupSession方法,跟进lookupSession你会发现,里面也有session的校验,如下图所示:
所以session的校验,不只是定制任务在执行,很多session的操作都有做session的校验。