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

文章来源原创   作者:临窗旋墨   发布时间:2020-12-11   阅读:890   标签:源码

[toc]

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

  • 20201211 Vic.xu

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

一、SecurityManager

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

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

一、session的创建

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

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

AbstractNativeSessionManager#start创建session方法如下

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

AbstractValidatingSessionManager#createSession

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

二 session的定时清除

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

AbstractValidatingSessionManager#enableSessionValidationIfNecessary方法说明

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

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

SimpleSession部分源码查看

  1. public class SimpleSession implements ValidatingSession, Serializable {
  2. //.....
  3. //session Id
  4. private transient Serializable id;
  5. //session的创建时间
  6. private transient Date startTimestamp;
  7. //session的停止时间
  8. private transient Date stopTimestamp;
  9. //session的最近一次访问时间,初始值是startTimestamp
  10. private transient Date lastAccessTime;
  11. //session的有效时长,默认30分钟
  12. private transient long timeout;
  13. //session是否到期
  14. private transient boolean expired;
  15. //主机
  16. private transient String host;
  17. //存放的属性 session.setAttributes存入的属性
  18. private transient Map<Object, Object> attributes;
  19. //根据最后访问时间和有效时间判断 是否过期▲
  20. protected boolean isTimedOut() {
  21. //代码略
  22. }
  23. //更新最后访问时间 ▲
  24. public void touch() {
  25. this.lastAccessTime = new Date();
  26. }
  27. //session检验▲
  28. public void validate() throws InvalidSessionException {
  29. //check for stopped: stopTimestamp不为空 ▲
  30. if (isStopped()) {
  31. //timestamp is set, so the session is considered stopped:
  32. throw new StoppedSessionException(msg);
  33. }
  34. //check for expiration 过期了 ▲
  35. if (isTimedOut()) {
  36. //设置 expired = true
  37. expire();
  38. //throw an exception explaining details of why it expired:
  39. throw new ExpiredSessionException(msg);
  40. }
  41. }
  42. }

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

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

ExecutorServiceSessionValidationScheduler#enableSessionValidation

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

ExecutorServiceSessionValidationScheduler#run

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

AbstractValidatingSessionManager的validateSessions

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

AbstractValidatingSessionManager#validate ▲▲▲

  1. protected void validate(Session session, SessionKey key) throws InvalidSessionException {
  2. try {
  3. doValidate(session);
  4. } catch (ExpiredSessionException ese) {
  5. //从sessionDao中删除过期的session
  6. onExpiration(session, ese, key);
  7. throw ese;
  8. } catch (InvalidSessionException ise) {
  9. //从sessionDao中删除不合法的session
  10. onInvalidation(session, ise, key);
  11. throw ise;
  12. }
  13. }
  1. doValidate方法 调用上文的SimpleSession中的validate进行校验,它会在过期以及已经停止的情况下抛出异常
  1. if (session instanceof ValidatingSession) {
  2. ((ValidatingSession) session).validate();
  3. } else {
  4. throw new IllegalStateException(msg);
  5. }
  1. onExpiration方法从sessionDao中删除session▲▲▲

    1. protected void onExpiration(Session s, ExpiredSessionException ese, SessionKey key) {
    2. onExpiration(s);
    3. notifyExpiration(s);
    4. afterExpired(s);
    5. }

    afterExpired调用的是DefaultSessionManager的afterExpired方法

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

其他-登出时时候如何删除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自身对检验

发表评论

目录