• shiro是如何清除过期session的(源码版本shiro1.6)


    shiro是如何清除过期session的(源码版本shiro1.6)

    由于项目中的shiro的缓存管理器替换为redis,故此处简略追踪记录过期session的销毁过程。

    一、SecurityManager

    安全管理器SecurityManager可以说是shiro最最重要的组件

    • 所有的安全交互都和它相关
    • 管理着所有的Subject
    • CacheManager交由它管理
    • Realm也交由它管理
    • SessionManager也由它管理

    一、session的创建

    session的创建的入口是SessionsSecurityManager#start,它进而调用SessionManager的start方法

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

    AbstractNativeSessionManager#start创建session方法如下

    public Session start(SessionContext context) {
        // 创建session:SimpleSession
        Session session = createSession(context);
        //设置session的timeout时间:默认30分钟
        applyGlobalSessionTimeout(session);
        //根据sessionId 生成cookie存入到request response中
        onStart(session, context);
        //通知session监听器
        notifyStart(session);
        //Don't expose the EIS-tier Session object to the client-tier:
        //把SimpleSession包装为DelegatingSession
        return createExposedSession(session, context);
    }
    

    AbstractValidatingSessionManager#createSession

    protected Session createSession(SessionContext context) throws AuthorizationException {
            enableSessionValidationIfNecessary();
            return doCreateSession(context);
        }
    
    doCreateSession方法简要说明
    1. 通过SimpleSessionFactory创建session实例:SimpleSession
    2. 通过SessionDAO缓存session;
      1. 默认MemorySessionDAO存储在内存中的ConcurrentMap
      2. 根据配置的缓存管理器,缓存session,我们的项目使用的是EnterpriseCacheSessionDAO,并为此dao配置了自定义的cacheManager(基于redis的)

    二 session的定时清除

    在上文创建session的时候首选执行的是方法是enableSessionValidationIfNecessary,它开启了一个定时器

    AbstractValidatingSessionManager#enableSessionValidationIfNecessary方法说明

    private void enableSessionValidationIfNecessary() {
        //获取session校验调度器
        SessionValidationScheduler scheduler = getSessionValidationScheduler();
        if (isSessionValidationSchedulerEnabled() && (scheduler == null || !scheduler.isEnabled())) {
            //启用session检验
            enableSessionValidation();
        }
    }
    

    在追踪定时器源码之前,我们先看一下SimpleSession的基本属性有哪些

    SimpleSession部分源码查看

    public class SimpleSession implements ValidatingSession, Serializable {
        //.....
        //session Id
        private transient Serializable id;
        //session的创建时间
        private transient Date startTimestamp;
        //session的停止时间
        private transient Date stopTimestamp;
        //session的最近一次访问时间,初始值是startTimestamp
        private transient Date lastAccessTime;
        //session的有效时长,默认30分钟
        private transient long timeout;
        //session是否到期
        private transient boolean expired;
        //主机
        private transient String host;
        //存放的属性  session.setAttributes存入的属性
        private transient Map<Object, Object> attributes;
        
        //根据最后访问时间和有效时间判断 是否过期▲
        protected boolean isTimedOut() {
            //代码略
        }
        //更新最后访问时间 ▲
        public void touch() {
            this.lastAccessTime = new Date();
        }
        
        //session检验▲
        public void validate() throws InvalidSessionException {
            //check for stopped: stopTimestamp不为空 ▲
            if (isStopped()) {
                //timestamp is set, so the session is considered stopped:
                throw new StoppedSessionException(msg);
            }
            //check for expiration  过期了 ▲
            if (isTimedOut()) {
                //设置 expired  = true
                expire();
                //throw an exception explaining details of why it expired:
      
                throw new ExpiredSessionException(msg);
            }
        }
    }
    

    了解了SimpleSession的基本结构我,我们继续查看session检验定时器

    上文的enableSessionValidation方法进入到的是ExecutorServiceSessionValidationScheduler的enableSessionValidation方法

    ExecutorServiceSessionValidationScheduler#enableSessionValidation

    • 默认每隔一小时执行一次run方法
    public void enableSessionValidation() {    
            // 创建ScheduledExecutorService
            this.service = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {  
    	       //.....
    	        }  
            });    
                // 初始化service interval时长之后开始执行this的run方法,每隔interval执行一次;
            this.service.scheduleAtFixedRate(this, interval, interval, TimeUnit.MILLISECONDS);
        }
        this.enabled = true;
    }
    

    ExecutorServiceSessionValidationScheduler#run

    public void run() {
           //....
           this.sessionManager.validateSessions();
           //....
        }
    
    
    • 进入AbstractValidatingSessionManager的validateSessions方法

    AbstractValidatingSessionManager的validateSessions

    public void validateSessions() {
        //....
    	//获取sessionDao中的全部session
        Collection<Session> activeSessions = getActiveSessions();
    	//分别校验每个session	
        for (Session s : activeSessions) {
           SessionKey key = new DefaultSessionKey(s.getId());
            //真正的校验方法
           validate(s, key);               
        }
    }
    

    AbstractValidatingSessionManager#validate ▲▲▲

    protected void validate(Session session, SessionKey key) throws InvalidSessionException {
            try {
                doValidate(session);
            } catch (ExpiredSessionException ese) {
                //从sessionDao中删除过期的session
                onExpiration(session, ese, key);
                throw ese;
            } catch (InvalidSessionException ise) {
                //从sessionDao中删除不合法的session
                onInvalidation(session, ise, key);
                throw ise;
            }
        }
    
    1. doValidate方法 调用上文的SimpleSession中的validate进行校验,它会在过期以及已经停止的情况下抛出异常

      if (session instanceof ValidatingSession) {
             ((ValidatingSession) session).validate();
          } else {     
              throw new IllegalStateException(msg);
          }
      
    2. onExpiration方法从sessionDao中删除session▲▲▲

      protected void onExpiration(Session s, ExpiredSessionException ese, SessionKey key) {
              onExpiration(s);
              notifyExpiration(s);
              afterExpired(s);
      }
      

      afterExpired调用的是DefaultSessionManager的afterExpired方法

    DefaultSessionManager#afterExpired:通过SessionDao中删除session
    • 或者是从内存中直接删除session
    • 或者调用缓存管理器的remove方法
    protected void afterExpired(Session session) {
        if (isDeleteInvalidSessions()) {
            delete(session);
        }
     }
    

    其他-登出时时候如何删除session的

    1. DelegatingSubject#logout →
    2. DefaultSecurityManager#logout →
      • stopSession(subject);即调用SimpleSession的stop方法

    其他-session最后操作时间如何更新

    • 每次进入ShiroFilter都如自动调用
    • ShiroFilter 的父类AbstractShiroFilter在执行doFilterInternal方法 的时候会调用updateSessionLastAccessTime方法,在其内部执行了 session.touch();
    • AbstractShiroFilter的doFilterInternal调用时机在其父类OncePerRequestFilter中的doFilter方法内,它会调用doFilterInternal方法

    其他-AbstractNativeSessionManager中的lookupSession中的session校验

    • AbstractNativeSessionManager 对session的相关操作(如属性操作/设置过期时间/stop/touch等等等)均为调用lookupSession方法
    • 在lookupSession方法中调用doGetSession方法。
    • 调用的就是AbstractValidatingSessionManager 的validate方法,参见上文定时器作为入口时,对session的清除等处理

    总结:

    • 了解shiro中最重要的对象securityManager是一个大管家,它管理着shiro生命周期的几乎所有的关联对象。

    • sessionManager的相关管理了session相关的所有操作,包括对session检验的定时器的定义,同时在session其他的操作的时候也会检验session的有效性

    • SimpleSession自身对检验

  • 相关阅读:
    最大比例(压轴题 )
    HDU-1016-素数环
    HDU-1241-油藏
    POJ-2251-地下城
    UVa-12096-集合栈计算机
    UVa-156-反片语
    UVa-10815-安迪的第一个字典
    UVa-101-木块问题
    UVa-10474-大理石在哪
    HDU-2955-Robberies
  • 原文地址:https://www.cnblogs.com/xuqiudong/p/14120067.html
Copyright © 2020-2023  润新知