学习 准备 尝试 谨慎小心

0%

Shiro源码解析之SessionManager02-SessionDAO

了解 SessionDAO

类继承图

image-20191028104313016

SessionDAO

1571367544804

AbstractSessionDAO

1571367836411
SessionIdGenerator sessionIdGenerator 用来生成 sessionId;

CachingSessionDAO

省略 getter 和 setter
1571368167696

EnterpriseCachingSessionDAO

1571368312829

MemorySessionDAO

1571369743342

理解 MemorySessionDAO

create(Session): Serializable

1. AbstractSessionDAO | create()

1
   // line 85
2
/**
3
   * Generates a new ID to be applied to the specified {@code session} instance.  This method is usually called
4
   * from within a subclass's {@link #doCreate} implementation where they assign the returned id to the session
5
   * instance and then create a record with this ID in the EIS data store.
6
   * <p/>
7
   * Subclass implementations backed by EIS data stores that auto-generate IDs during record creation, such as
8
   * relational databases, don't need to use this method or the {@link #getSessionIdGenerator() sessionIdGenerator}
9
   * attribute - they can simply return the data store's generated ID from the {@link #doCreate} implementation
10
   * if desired.
11
   * <p/>
12
   * This implementation uses the {@link #setSessionIdGenerator configured} {@link SessionIdGenerator} to create
13
   * the ID.
14
   *
15
   * @param session the new session instance for which an ID will be generated and then assigned
16
   * @return the generated ID to assign
17
   */
18
  protected Serializable generateSessionId(Session session) {
19
      if (this.sessionIdGenerator == null) {
20
          String msg = "sessionIdGenerator attribute has not been configured.";
21
          throw new IllegalStateException(msg);
22
      }
23
      return this.sessionIdGenerator.generateId(session);
24
  }
25
	
26
  /**
27
   * Creates the session by delegating EIS creation to subclasses via the {@link #doCreate} method, and then
28
   * asserting that the returned sessionId is not null.
29
   *
30
   * @param session Session object to create in the EIS and associate with an ID.
31
   */
32
  public Serializable create(Session session) {
33
// 调用 doCreate(),返回 sessionId
34
      Serializable sessionId = doCreate(session);
35
      verifySessionId(sessionId);
36
      return sessionId;
37
  }
38
  /**
39
   * Ensures the sessionId returned from the subclass implementation of {@link #doCreate} is not null and not
40
   * already in use.
41
   *
42
   * @param sessionId session id returned from the subclass implementation of {@link #doCreate}
43
   */
44
  private void verifySessionId(Serializable sessionId) {
45
      if (sessionId == null) {
46
          String msg = "sessionId returned from doCreate implementation is null.  Please verify the implementation.";
47
          throw new IllegalStateException(msg);
48
      }
49
  }
50
  ......
51
  // line 156
52
  /**
53
   * Subclass hook to actually persist the given <tt>Session</tt> instance to the underlying EIS.
54
   *
55
   * @param session the Session instance to persist to the EIS.
56
   * @return the id of the session created in the EIS (i.e. this is almost always a primary key and should be the
57
   *         value returned from {@link org.apache.shiro.session.Session#getId() Session.getId()}.
58
   */
59
  protected abstract Serializable doCreate(Session session);

2. MemorySessionDAO | doCreate()

1
  // line 66
2
  protected Serializable doCreate(Session session) {
3
// 生成sessionId 
4
      Serializable sessionId = generateSessionId(session);
5
// 将sessionId填充到 session对象中
6
      assignSessionId(session, sessionId);
7
// 将session存储到ConcurrentHashMap对象中
8
      storeSession(sessionId, session);
9
      return sessionId;
10
  }
11
12
  protected Session storeSession(Serializable id, Session session) {
13
      if (id == null) {
14
          throw new NullPointerException("id argument cannot be null.");
15
      }
16
      return sessions.putIfAbsent(id, session);
17
  }

ConcurrentHashMap 是线程安全的 HashMap

小结

  1. 生成 sessionId (MemorySessionDAO)
  2. 将 sessionId 填充到 session中(MemorySessionDAO)
  3. 将 session 存储到 ConcurrentHashMap 对象sessions中,sessionId 作为key(MemorySessionDAO)
  4. 验证 sessionId 是否为空
  5. 返回 sessionId

readSession(Serializable): Session

1. AbstractSessionDAO | readSession()

1
// line 158
2
/**
3
    * Retrieves the Session object from the underlying EIS identified by <tt>sessionId</tt> by delegating to
4
    * the {@link #doReadSession(java.io.Serializable)} method.  If {@code null} is returned from that method, an
5
    * {@link UnknownSessionException} will be thrown.
6
    *
7
    * @param sessionId the id of the session to retrieve from the EIS.
8
    * @return the session identified by <tt>sessionId</tt> in the EIS.
9
    * @throws UnknownSessionException if the id specified does not correspond to any session in the EIS.
10
    */
11
   public Session readSession(Serializable sessionId) throws UnknownSessionException {
12
       Session s = doReadSession(sessionId);
13
       if (s == null) {
14
           throw new UnknownSessionException("There is no session with id [" + sessionId + "]");
15
       }
16
       return s;
17
   }
18
19
   /**
20
    * Subclass implementation hook that retrieves the Session object from the underlying EIS or {@code null} if a
21
    * session with that ID could not be found.
22
    *
23
    * @param sessionId the id of the <tt>Session</tt> to retrieve.
24
    * @return the Session in the EIS identified by <tt>sessionId</tt> or {@code null} if a
25
    *         session with that ID could not be found.
26
    */
27
   protected abstract Session doReadSession(Serializable sessionId);

2. MemorySessionDAO | doReadSession()

1
// line 80
2
   protected Session doReadSession(Serializable sessionId) {
3
       return sessions.get(sessionId);
4
   }

update(Session): void

MemorySessionDAO | update()

1
// line 72
2
protected Session storeSession(Serializable id, Session session) {
3
    if (id == null) {
4
        throw new NullPointerException("id argument cannot be null.");
5
    }
6
    return sessions.putIfAbsent(id, session);
7
}	
8
// line 84
9
public void update(Session session) throws UnknownSessionException {
10
    storeSession(session.getId(), session);
11
}

delete(Session): void

MemorySessionDAO | delete()

1
// line 88
2
   public void delete(Session session) {
3
       if (session == null) {
4
           throw new NullPointerException("session argument cannot be null.");
5
       }
6
       Serializable id = session.getId();
7
       if (id != null) {
8
           sessions.remove(id);
9
       }
10
   }

理解 EnterpiseCachingSessionDAO

create(Session): Serializable

1. CachingSessionDAO 中重写了 readSession()

1
   // line 177
2
/**
3
    * Calls {@code super.create(session)}, then caches the session keyed by the returned {@code sessionId}, and then
4
    * returns this {@code sessionId}.
5
    *
6
    * @param session Session object to create in the EIS and then cache.
7
    */
8
   public Serializable create(Session session) {
9
       // 调用父类的create()方法,参考 MemorySessionDAO 中 AbstractSessionDAO 的 create() 方法
10
       // 内部调用 doCreate() ,然后验证 sessionId
11
       Serializable sessionId = super.create(session);
12
	// 缓存session
13
       cache(session, sessionId);
14
       return sessionId;
15
   }

2. EnterpriseCachingSessioDAO | doCreate()

1
// line 63
2
   protected Serializable doCreate(Session session) {
3
       Serializable sessionId = generateSessionId(session);
4
       assignSessionId(session, sessionId);
5
       return sessionId;
6
   }

3. CachingSessionDAO | cache(session, sessionId)

1
   ......
2
// line 141
3
/**
4
    * Returns the active sessions cache, but if that cache instance is null, first lazily creates the cache instance
5
    * via the {@link #createActiveSessionsCache()} method and then returns the instance.
6
    * <p/>
7
    * Note that this method will only return a non-null value code if the {@code CacheManager} has been set.  If
8
    * not set, there will be no cache.
9
    *
10
    * @return the active sessions cache instance.
11
    */
12
   private Cache<Serializable, Session> getActiveSessionsCacheLazy() {
13
       if (this.activeSessions == null) {
14
           this.activeSessions = createActiveSessionsCache();
15
       }
16
       return activeSessions;
17
   }
18
19
20
// line 157
21
/**
22
    * Creates a cache instance used to store active sessions.  Creation is done by first
23
    * {@link #getCacheManager() acquiring} the {@code CacheManager}.  If the cache manager is not null, the
24
    * cache returned is that resulting from the following call:
25
    * <pre>       String name = {@link #getActiveSessionsCacheName() getActiveSessionsCacheName()};
26
    * cacheManager.getCache(name);</pre>
27
    *
28
    * @return a cache instance used to store active sessions, or {@code null} if the {@code CacheManager} has
29
    *         not been set.
30
    */
31
   protected Cache<Serializable, Session> createActiveSessionsCache() {
32
       Cache<Serializable, Session> cache = null;
33
       CacheManager mgr = getCacheManager();
34
       if (mgr != null) {
35
           String name = getActiveSessionsCacheName();
36
           cache = mgr.getCache(name);
37
       }
38
       return cache;
39
   }
40
41
......
42
43
// line 220
44
/**
45
    * Caches the specified session under the cache entry key of {@code sessionId}.
46
    *
47
    * @param session   the session to cache
48
    * @param sessionId the session id, to be used as the cache entry key.
49
    * @since 1.0
50
    */
51
   protected void cache(Session session, Serializable sessionId) {
52
       if (session == null || sessionId == null) {
53
           return;
54
       }
55
       // 获取 cache 
56
       Cache<Serializable, Session> cache = getActiveSessionsCacheLazy();
57
       if (cache == null) {
58
           return;
59
       }
60
       // 将 session 存储到 cache 中,sessionId 作为 key
61
       cache(session, sessionId, cache);
62
   }
63
64
   /**
65
    * Caches the specified session in the given cache under the key of {@code sessionId}.  This implementation
66
    * simply calls {@code cache.put(sessionId,session)} and can be overridden for custom behavior.
67
    *
68
    * @param session   the session to cache
69
    * @param sessionId the id of the session, expected to be the cache key.
70
    * @param cache     the cache to store the session
71
    */
72
   protected void cache(Session session, Serializable sessionId, Cache<Serializable, Session> cache) {
73
       cache.put(sessionId, session);
74
   }

EnterpriseCachingSessionDAO 构造函数中设置了默认的 CacheManager

1
public EnterpriseCacheSessionDAO() {
2
    setCacheManager(new AbstractCacheManager() {
3
        @Override
4
        protected Cache<Serializable, Session> createCache(String name) throws CacheException {
5
            return new MapCache<Serializable, Session>(name, new ConcurrentHashMap<Serializable, Session>());
6
        }
7
    });
8
}

从中可以看出,实际中 MapCache 内部也是使用 ConcurrentHashMap对象来存储session的。

readSession(Serializable): Session

1. CachingSessionDAO | readSession()

1
   // line 250
2
/**
3
    * Attempts to acquire the Session from the cache first using the session ID as the cache key.  If no session
4
    * is found, {@code super.readSession(sessionId)} is called to perform the actual retrieval.
5
    *
6
    * @param sessionId the id of the session to retrieve from the EIS.
7
    * @return the session identified by {@code sessionId} in the EIS.
8
    * @throws UnknownSessionException if the id specified does not correspond to any session in the cache or EIS.
9
    */
10
   public Session readSession(Serializable sessionId) throws UnknownSessionException {
11
       // 根据 sessionId 获取缓存的 session
12
       Session s = getCachedSession(sessionId);
13
       if (s == null) {
14
           // 调用父类方法 readSession 获取 session
15
           // 其内部调用 doReadSession() 获取session
16
           s = super.readSession(sessionId);
17
       }
18
       return s;
19
   }
20
......
21
       
22
// line 189
23
   /**
24
    * Returns the cached session with the corresponding {@code sessionId} or {@code null} if there is
25
    * no session cached under that id (or if there is no Cache).
26
    *
27
    * @param sessionId the id of the cached session to acquire.
28
    * @return the cached session with the corresponding {@code sessionId}, or {@code null} if the session
29
    *         does not exist or is not cached.
30
    */
31
   protected Session getCachedSession(Serializable sessionId) {
32
       Session cached = null;
33
       if (sessionId != null) {
34
           Cache<Serializable, Session> cache = getActiveSessionsCacheLazy();
35
           if (cache != null) {
36
               cached = getCachedSession(sessionId, cache);
37
           }
38
       }
39
       return cached;
40
   }
41
42
   /**
43
    * Returns the Session with the specified id from the specified cache.  This method simply calls
44
    * {@code cache.get(sessionId)} and can be overridden by subclasses for custom acquisition behavior.
45
    *
46
    * @param sessionId the id of the session to acquire.
47
    * @param cache     the cache to acquire the session from
48
    * @return the cached session, or {@code null} if the session wasn't in the cache.
49
    */
50
   protected Session getCachedSession(Serializable sessionId, Cache<Serializable, Session> cache) {
51
       return cache.get(sessionId);
52
   }

EnterpriseCachingSessionDAO 中实现了 doReadSession()

1
protected Session doReadSession(Serializable sessionId) {
2
    return null; //should never execute because this implementation relies on parent class to access cache, which
3
    //is where all sessions reside - it is the cache implementation that determines if the
4
    //cache is memory only or disk-persistent, etc.
5
}

update(Session): void

1. CachingSessionDAO 中实现了 update()

1
// line 266
2
   /**
3
    * Updates the state of the given session to the EIS by first delegating to
4
    * {@link #doUpdate(org.apache.shiro.session.Session)}.  If the session is a {@link ValidatingSession}, it will
5
    * be added to the cache only if it is {@link ValidatingSession#isValid()} and if invalid, will be removed from the
6
    * cache.  If it is not a {@code ValidatingSession} instance, it will be added to the cache in any event.
7
    *
8
    * @param session the session object to update in the EIS.
9
    * @throws UnknownSessionException if no existing EIS session record exists with the
10
    *                                 identifier of {@link Session#getId() session.getId()}
11
    */
12
   public void update(Session session) throws UnknownSessionException {
13
       doUpdate(session);
14
	// 判断session是否是 ValidatingSession 实例
15
       if (session instanceof ValidatingSession) {
16
		// 判断 session 是否有效
17
           if (((ValidatingSession) session).isValid()) {
18
               cache(session, session.getId());
19
           } else {
20
               uncache(session);
21
           }
22
       } else {
23
           cache(session, session.getId());
24
       }
25
   }

2. EnterpriseCachingSessionDAO | doUpdate()

1
protected void doUpdate(Session session) {
2
    //does nothing - parent class persists to cache.
3
}

delete(Session): void

1. CachingSessionDAO | delete()

1
/**
2
 * Removes the specified session from any cache and then permanently deletes the session from the EIS by
3
 * delegating to {@link #doDelete}.
4
 *
5
 * @param session the session to remove from caches and permanently delete from the EIS.
6
 */
7
public void delete(Session session) {
8
	// 删除缓存中的session
9
    uncache(session);
10
    // 调用 doDelete()
11
    doDelete(session);
12
}

2. EnterpriseCachingSessionDAO | doDelete()

1
protected void doDelete(Session session) {
2
    //does nothing - parent class removes from cache.
3
}

总结

SessionDAO用于 session 存储,它有两个实现:

  • MemorySessionDAO 使用 ConcurrentHashMap 对象将 session 保存到内存中
  • EnterpriseCachingSessionDAO 将session保存到缓存中(该缓存默认使用 ConcurrentHashMap 保存到内存中)。其实 EnterpriseCachingSessionDAO 就是一个对外的接口,用户自定义SessionDAO时,只需继承他。