shiro源码篇-shiro的session的查询、刷新、过期与删除,你值得拥有
开⼼⼀刻 
  ⽼公酷爱⽹络游戏,⽼婆⽆奈,只得告诫他:你玩就玩了,但是千万不可以在游戏⾥⽼婆,不然,哼哼。。。
  ⽼公嘴⾓露出了微笑:放⼼吧亲爱的,我绝对不会在游戏⾥⽼婆的!因为我有⽼公!
  ⽼婆:......
前情回顾
  ⼤家还记得讲了什么吗,我们来⼀起简单回顾下:
    SecurityManager是shiro的核⼼,负责与shiro的其他组件进⾏交互;SessionManager是session的真正管理者,负责shiro的session管理;
    SessionsSecurityManager的start⽅法中将session的创建委托给了具体的sessionManager,是创建session的关键⼊⼝。
    SimpleSession是shiro完完全全的⾃⼰实现,是shiro对session的⼀种拓展;实现了ValidatingSession接⼝,具有⾃我校验的功能;⼀般不对外暴露,暴露的往往是他的代理:DelegatingSession;SimpleSession有⼏个属性值得重点关注下,如下
        id:就是session id;
        startTimestamp:session的创建时间;
        stopTimestamp:session的失效时间;
        lastAccessTime:session的最近⼀次访问时间,初始值是startTimestamp
        timeout:session的有效时长,默认30分钟
        expired:session是否到期
        attributes:session的属性容器
查询
  session的创建完成后,会将session(SimpleSession类型)对象的代理对象(DelegatingSession)
装饰成StoppingAwareProxiedSession对象,然后绑定到subject(类型是DelegatingSubject);
  Session session = Session();返回的就是绑定在当前subjuct的session。注意subject的实际类型是:DelegatingSubject,如下图
刷新
  shiro的Session接⼝提供了⼀个touch⽅法,负责session的刷新;session的代理对象最终会调⽤SimpleSession的touch():
public void touch() {
el表达式获取session中的值this.lastAccessTime = new Date();        // 更新最后被访问时间为当前时间
}
  但是touch⽅法是什么时候被调⽤的呢?JavaSE需要我们⾃⼰定期的调⽤session的touch() 去更新最后访问时间;如果是Web应⽤,每次进⼊ShiroFilter都会⾃动调⽤uch()来更新最后访问时间,ShiroFilter的类图如下:
  ShiroFilter⾃动调⽤uch()如下
过期
  如果是让我们⾃⼰实现session过期的判断,我们会怎么做了?我们来看看shiro是怎么做的,或许我们能够从中学到⼀些经验。
    还记得AbstractValidatingSessionManager中createSession⽅法吗?在调⽤doCreateSession⽅法之前调⽤
enableSessionValidationIfNecessary(),enableSessionValidationIfNecessary代码如下
private void enableSessionValidationIfNecessary() {
// 获取session验证调度器
SessionValidationScheduler scheduler = getSessionValidationScheduler();
// session验证调度器开启 && (调度器为空或调度器不可⽤)
if (isSessionValidationSchedulerEnabled() && (scheduler == null || !scheduler.isEnabled())) {
enableSessionValidation();        // 开启session验证
}
}
View Code
    第⼀次创建session的时候,如果session验证调度器启⽤(默认是启⽤),那么调⽤enableSessionValidation(),enableSessionValidation代码如下
protected synchronized void enableSessionValidation() {
SessionValidationScheduler scheduler = getSessionValidationScheduler();        // 获取调取器
if (scheduler == null) {
scheduler = createSessionValidationScheduler();                            // 创建调取器,实际类型是ExecutorServiceSessionValidationScheduler
setSessionValidationScheduler(scheduler);                                // 将调度器绑定到sessionManager
}
// it is possible that that a scheduler was already created and set via 'setSessionValidationScheduler()'
// but would not have been enabled/started yet
if (!scheduler.isEnabled()) {
if (log.isInfoEnabled()) {
log.info("Enabling session ");
}
afterSessionValidationEnabled();                                        // 什么也没做,供继承,便于拓展
}
}
View Code
    ExecutorServiceSessionValidationScheduler类图如下,它实现了Runnable接⼝
  调⽤scheduler的enableSessionValidation(),enableSessionValidation⽅法如下
public void enableSessionValidation() {
if (this.interval > 0l) {
// 创建ScheduledExecutorService
this.service = wSingleThreadScheduledExecutor(new ThreadFactory() {
private final AtomicInteger count = new AtomicInteger(1);
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setDaemon(true);
thread.setName(threadNamePrefix + AndIncrement());
return thread;
}
});
/
/  初始化service interval时长之后开始执⾏this的run⽅法,每隔interval执⾏⼀次;注意interval的单位是TimeUnit.MILLISECONDS
this.service.scheduleAtFixedRate(this, interval, interval, TimeUnit.MILLISECONDS);    // this就是ExecutorServiceSessionValidationScheduler⾃⼰
}
}
View Code
  定时(默认每隔60分钟)的调⽤ExecutorServiceSessionValidationScheduler的run⽅法,run⽅法中调⽤sessionManager的validateSessions⽅法来完成session的验证,validateSessions⽅法如下
/
**
* @see ValidatingSessionManager#validateSessions()
*/
public void validateSessions() {
if (log.isInfoEnabled()) {
log.info("Validating all ");
}
int invalidCount = 0;
// 从sessionDao中获取全部的session
// sessionDao可以是默认的MemorySessionDAO,也可以是我们定制的CachingSessionDAO
Collection<Session> activeSessions = getActiveSessions();
if (activeSessions != null && !activeSessions.isEmpty()) {
// ⼀个⼀个校验
for (Session s : activeSessions) {
try {
//simulate a lookup key to satisfy the method signature.
//this could probably stand to be cleaned up in future versions:
SessionKey key = new Id());
validate(s, key);        // 真正校验的⽅法
} catch (InvalidSessionException e) {
if (log.isDebugEnabled()) {
boolean expired = (e instanceof ExpiredSessionException);
String msg = "Invalidated session with id [" + s.getId() + "]" +
(expired ? " (expired)" : " (stopped)");
log.debug(msg);
}
invalidCount++;            // 统计上次到这次定时任务间隔内过期的session个数
}
}
}
if (log.isInfoEnabled()) {
String msg = "Finished session validation.";
if (invalidCount > 0) {
msg += "  [" + invalidCount + "] sessions were stopped.";
} else {
msg += "  No sessions were stopped.";
}
log.info(msg);
}
}
View Code
  validate⽅法如下
protected void validate(Session session, SessionKey key) throws InvalidSessionException {
try {
doValidate(session);                    // 真正校验session
} catch (ExpiredSessionException ese) {
onExpiration(session, ese, key);        // 从sessionDao中删除过期的session
throw ese;                                // 抛出异常供上层统计⽤
} catch (InvalidSessionException ise) {
onInvalidation(session, ise, key);        // 从sessionDao中删除不合法的session
throw ise;                                // 抛出异常供上层统计⽤
}
}
View Code
  通过捕获doValidate()抛出的异常来剔除过期的或不合法的session,并将异常接着往上抛,供上层统计过期数量。注意:ExpiredSessionException的⽗类是StoppedSessionException,⽽StoppedSessionException的⽗类是InvalidSessionException。
  doValidate⽅法如下
protected void doValidate(Session session) throws InvalidSessionException {
if (session instanceof ValidatingSession) {
((ValidatingSession) session).validate();        // 校验session是否过期
} else {                                            // 若session不是ValidatingSession类型,则抛出IllegalStateException异常
String msg = "The " + getClass().getName() + " implementation only supports validating " +
"Session implementations of the " + Name() + " interface.  " +
"Please either implement this interface in your session implementation or override the " +
Name() + ".doValidate(Session) method to perform validation.";
throw new IllegalStateException(msg);
}
}
View Code
    若session不是ValidatingSession类型,则抛出IllegalStateException异常
  validate⽅法如下

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。