sessionDao接口的源码如下,通过名字就可以知道方法的作用,就不翻译解释了
public interface SessionDAO {
Serializable create(Session session);
Session readSession(Serializable sessionId) throws UnknownSessionException;
void update(Session session) throws UnknownSessionException;
void delete(Session session);
Collection<Session> getActiveSessions();
}
在shiro中实现该接口的类的结构如如下
AbstractSessionDao,它对会话创建和读取执行一些健全性检查,并在需要时允许可插入自己的会话ID生成策略。
update和delete方法留给了子类实现,readSession和create方法也没有实现,代用了自己的抽象方法doReadSession和doCreate,需要子类实现。
/**
* An abstract {@code SessionDAO} implementation that performs some sanity checks on session creation and reading and
* allows for pluggable Session ID generation strategies if desired. The {@code SessionDAO}
* {@link SessionDAO#update update} and {@link SessionDAO#delete delete} methods are left to
* subclasses.
* 一个抽象的 {@code SessionDAO}实现,它对会话创建和读取执行一些健全性检查,并在需要时允许可插入的会话ID生成策略。
* {@code SessionDAO} {@link SessionDAO#update update} 和 {@link SessionDAO#delete delete}方法留给了子类。
*
* <h3>Session ID Generation</h3>
* This class also allows for plugging in a {@link SessionIdGenerator} for custom ID generation strategies. This is
* optional, as the default generator is probably sufficient for most cases. Subclass implementations that do use a
* generator (default or custom) will want to call the
* {@link #generateSessionId(org.apache.shiro.session.Session)} method from within their {@link #doCreate}
* implementations.
* 此类还允许插入 {@link SessionIdGenerator}以实现自定义ID生成策略。 这是可选的,因为默认生成器可能足以满足大多数情况。
* 使用生成器(默认或自定义)的子类实现将希望从 {@link #doCreate}实现中调用
* {@link #generateSessionId(org.apache.shiro.session.Session)}方法。
* <p/>
*
* Subclass implementations that rely on the EIS data store to generate the ID automatically (e.g. when the session
* ID is also an auto-generated primary key), they can simply ignore the {@code SessionIdGenerator} concept
* entirely and just return the data store's ID from the {@link #doCreate} implementation.
* 依赖于EIS数据存储自动生成ID的子类实现(例如,当会话ID也是自动生成的主键时),
* 他们可以完全忽略 {@code SessionIdGenerator}概念,只返回数据存储的ID {@link #doCreate}实现。
*
* @since 1.0
*/
public abstract class AbstractSessionDAO implements SessionDAO {
private SessionIdGenerator sessionIdGenerator;
public AbstractSessionDAO() {
this.sessionIdGenerator = new JavaUuidSessionIdGenerator();
}
public SessionIdGenerator getSessionIdGenerator() {
return sessionIdGenerator;
}
public void setSessionIdGenerator(SessionIdGenerator sessionIdGenerator) {
this.sessionIdGenerator = sessionIdGenerator;
}
protected Serializable generateSessionId(Session session) {
if (this.sessionIdGenerator == null) {
String msg = "sessionIdGenerator attribute has not been configured.";
throw new IllegalStateException(msg);
}
return this.sessionIdGenerator.generateId(session);
}
public Serializable create(Session session) {
Serializable sessionId = doCreate(session);
verifySessionId(sessionId);
return sessionId;
}
private void verifySessionId(Serializable sessionId) {
if (sessionId == null) {
String msg = "sessionId returned from doCreate implementation is null. Please verify the implementation.";
throw new IllegalStateException(msg);
}
}
protected void assignSessionId(Session session, Serializable sessionId) {
((SimpleSession) session).setId(sessionId);
}
/**
* Subclass hook to actually persist the given <tt>Session</tt> instance to the underlying EIS.
*
* @param session the Session instance to persist to the EIS.
* @return the id of the session created in the EIS (i.e. this is almost always a primary key and should be the
* value returned from {@link org.apache.shiro.session.Session#getId() Session.getId()}.
*/
protected abstract Serializable doCreate(Session session);
public Session readSession(Serializable sessionId) throws UnknownSessionException {
Session s = doReadSession(sessionId);
if (s == null) {
throw new UnknownSessionException("There is no session with id [" + sessionId + "]");
}
return s;
}
/**
* Subclass implementation hook that retrieves the Session object from the underlying EIS or {@code null} if a
* session with that ID could not be found.
*
* @param sessionId the id of the <tt>Session</tt> to retrieve.
* @return the Session in the EIS identified by <tt>sessionId</tt> or {@code null} if a
* session with that ID could not be found.
*/
protected abstract Session doReadSession(Serializable sessionId);
}
MemorySessionDao是通过ConcurrentHashMap来实现的基于内容的session的存储的,DefaultSessionManager默认使用的就是MemorySessionDao
/**
* Simple memory-based implementation of the SessionDAO that stores all of its sessions in an in-memory
* {@link ConcurrentMap}. <b>This implementation does not page to disk and is therefore unsuitable for applications
* that could experience a large amount of sessions</b> and would therefore cause {@code OutOfMemoryException}s. It is
* <em>not</em> recommended for production use in most environments.
* SessionDAO是简单基于内存的实现,它将所有会话存储在内存中 {@link ConcurrentMap}中。
* 此实现不会分页到磁盘,因此不适合可能遇到大量会话的应用程序,因此会导致 {@code OutOfMemoryException}。
* 不建议在大多数环境中进行生产使用。
*
* <h2>Memory Restrictions</h2>
* If your application is expected to host many sessions beyond what can be stored in the
* memory available to the JVM, it is highly recommended to use a different {@code SessionDAO} implementation which
* uses a more expansive or permanent backing data store.
* 如果您的应用程序需要承载超出可存储在JVM可用内存中的多个会话,
* 强烈建议使用不同的 {@code SessionDAO}实现,该实现使用更广泛或永久的后备数据存储。
* <p/>
*
* In this case, it is recommended to instead use a custom
* {@link CachingSessionDAO} implementation that communicates with a higher-capacity data store of your choice
* (file system, database, etc).
* 在这种情况下,建议使用自定义的 {@link CachingSessionDAO}实现,
* 该实现与您选择的更高容量的数据存储(文件系统,数据库等)进行通信。
*
* <h2>Changes in 1.0</h2>
* This implementation prior to 1.0 used to subclass the {@link CachingSessionDAO}, but this caused problems with many
* cache implementations that would expunge entries due to TTL settings, resulting in Sessions that would be randomly
* (and permanently) lost. The Shiro 1.0 release refactored this implementation to be 100% memory-based (without
* {@code Cache} usage to avoid this problem.
* 1.0之前的这个实现用于子类 {@link CachingSessionDAO},但是这导致了许多缓存实现的问题,这些缓存实现会因TTL设置而清除条目,
* 从而导致随机(和永久)丢失的Sessions。
* Shiro 1.0版本重构了这个实现是100%基于内存的用法来避免这个问题。
*
* @see CachingSessionDAO
* @since 0.1
*/
public class MemorySessionDAO extends AbstractSessionDAO {
private static final Logger log = LoggerFactory.getLogger(MemorySessionDAO.class);
private ConcurrentMap<Serializable, Session> sessions;
public MemorySessionDAO() {
this.sessions = new ConcurrentHashMap<Serializable, Session>();
}
protected Serializable doCreate(Session session) {
Serializable sessionId = generateSessionId(session);
assignSessionId(session, sessionId);
storeSession(sessionId, session);
return sessionId;
}
protected Session storeSession(Serializable id, Session session) {
if (id == null) {
throw new NullPointerException("id argument cannot be null.");
}
return sessions.putIfAbsent(id, session);
}
protected Session doReadSession(Serializable sessionId) {
return sessions.get(sessionId);
}
public void update(Session session) throws UnknownSessionException {
storeSession(session.getId(), session);
}
public void delete(Session session) {
if (session == null) {
throw new NullPointerException("session argument cannot be null.");
}
Serializable id = session.getId();
if (id != null) {
sessions.remove(id);
}
}
public Collection<Session> getActiveSessions() {
Collection<Session> values = sessions.values();
if (CollectionUtils.isEmpty(values)) {
return Collections.emptySet();
} else {
return Collections.unmodifiableCollection(values);
}
}
}
CachingSessionDAO也是一个抽象方法,继承了AbstractSessionDao,并且实现了CacheManagerAware方法。
/**
* An CachingSessionDAO is a SessionDAO that provides a transparent caching layer between the components that
* use it and the underlying EIS (Enterprise Information System) session backing store (for example, filesystem,
* database, enterprise grid/cloud, etc).
* CachingSessionDAO是一个SessionDAO,它在使用它的组件和底层EIS(企业信息系统)
* 会话存储(例如,文件系统,数据库,企业网格/云等)系统之间提供了一层透明的缓存层。
* 注:这样就可以减少对于底层缓存系统中对session的频繁读取等操作
* <p/>
*
* This implementation caches all active sessions in a configured
* {@link #getActiveSessionsCache() activeSessionsCache}. This property is {@code null} by default and if one is
* not explicitly set, a {@link #setCacheManager cacheManager} is expected to be configured which will in turn be used
* to acquire the {@code Cache} instance to use for the {@code activeSessionsCache}.
* 此实现将所有活动会话缓存在已配置的activeSessionsCache中。
* 默认情况下并且未明确设置activeSessions,此属性为 {@code null}。
* 需要配置 cacheManager,然后将通过getActiveSessionsCache来获取要用的缓存实例。
*
* <p/>
* All {@code SessionDAO} methods are implemented by this class to employ
* caching behavior and delegates the actual EIS operations to respective do* methods to be implemented by
* subclasses (doCreate, doRead, etc).
* 所有SessionDAO方法都由此类实现,以用来做缓存行为,
* 并将实际的EIS操作委托给由子类实现的各个do *方法(doCreate,doRead等)。
*
* @since 0.2
*/
public abstract class CachingSessionDAO extends AbstractSessionDAO implements CacheManagerAware {
/**
* The default active sessions cache name, equal to {@code shiro-activeSessionCache}.
*/
public static final String ACTIVE_SESSION_CACHE_NAME = "shiro-activeSessionCache";
/**
* The CacheManager to use to acquire the Session cache.
* cacheManager用于获取session缓存
*/
private CacheManager cacheManager;
/**
* The Cache instance responsible for caching Sessions.
* 用于缓存session的Cache实例
*/
private Cache<Serializable, Session> activeSessions;
/**
* The name of the session cache, defaults to {@link #ACTIVE_SESSION_CACHE_NAME}.
* session缓存的名字
*/
private String activeSessionsCacheName = ACTIVE_SESSION_CACHE_NAME;
/**
* Default no-arg constructor.
*/
public CachingSessionDAO() {
}
/**
* Sets the cacheManager to use for acquiring the {@link #getActiveSessionsCache() activeSessionsCache} if
* one is not configured.
*
* @param cacheManager the manager to use for constructing the session cache.
*/
public void setCacheManager(CacheManager cacheManager) {
this.cacheManager = cacheManager;
}
public CacheManager getCacheManager() {
return cacheManager;
}
public String getActiveSessionsCacheName() {
return activeSessionsCacheName;
}
/**
* Sets the name of the active sessions cache to be returned by the {@code CacheManager}. Defaults to
* {@link #ACTIVE_SESSION_CACHE_NAME}.
*
* @param activeSessionsCacheName the name of the active sessions cache to be returned by the {@code CacheManager}.
*/
public void setActiveSessionsCacheName(String activeSessionsCacheName) {
this.activeSessionsCacheName = activeSessionsCacheName;
}
/**
* Returns the cache instance to use for storing active sessions. If one is not available (it is {@code null}),
* it will be {@link CacheManager#getCache(String) acquired} from the {@link #setCacheManager configured}
* {@code CacheManager} using the {@link #getActiveSessionsCacheName() activeSessionsCacheName}.
*
* @return the cache instance to use for storing active sessions or {@code null} if the {@code Cache} instance
* should be retrieved from the
*/
public Cache<Serializable, Session> getActiveSessionsCache() {
return this.activeSessions;
}
/**
* Sets the cache instance to use for storing active sessions. If one is not set (it remains {@code null}),
* it will be {@link CacheManager#getCache(String) acquired} from the {@link #setCacheManager configured}
* {@code CacheManager} using the {@link #getActiveSessionsCacheName() activeSessionsCacheName}.
*
* @param cache the cache instance to use for storing active sessions or {@code null} if the cache is to be
* acquired from the {@link #setCacheManager configured} {@code CacheManager}.
*/
public void setActiveSessionsCache(Cache<Serializable, Session> cache) {
this.activeSessions = cache;
}
/**
* Returns the active sessions cache, but if that cache instance is null, first lazily creates the cache instance
* via the {@link #createActiveSessionsCache()} method and then returns the instance.
* <p/>
* Note that this method will only return a non-null value code if the {@code CacheManager} has been set. If
* not set, there will be no cache.
* 返回活动会话缓存,但如果该缓存实例为null,
* 则首先通过 {@link #createActiveSessionsCache()}方法懒惰地创建缓存实例,然后返回实例。
* 请注意,如果已设置 {@code CacheManager},则此方法仅返回非空值代码。 如果未设置,则不会有缓存。
*
* @return the active sessions cache instance.
*/
private Cache<Serializable, Session> getActiveSessionsCacheLazy() {
if (this.activeSessions == null) {
this.activeSessions = createActiveSessionsCache();
}
return activeSessions;
}
/**
*
* @return a cache instance used to store active sessions, or {@code null} if the {@code CacheManager} has
* not been set.
* 返回一个实例用来存储活动会话,如果没有设置cacheManger,则返回null,也就是不缓存活动会话
*/
protected Cache<Serializable, Session> createActiveSessionsCache() {
Cache<Serializable, Session> cache = null;
CacheManager mgr = getCacheManager();
if (mgr != null) {
String name = getActiveSessionsCacheName();
cache = mgr.getCache(name);
}
return cache;
}
/**
* Calls {@code super.create(session)}, then caches the session keyed by the returned {@code sessionId}, and then
* returns this {@code sessionId}.
*
* @param session Session object to create in the EIS and then cache.
*/
public Serializable create(Session session) {
Serializable sessionId = super.create(session);
cache(session, sessionId);
return sessionId;
}
/**
* Returns the cached session with the corresponding {@code sessionId} or {@code null} if there is
* no session cached under that id (or if there is no Cache).
*
* @param sessionId the id of the cached session to acquire.
* @return the cached session with the corresponding {@code sessionId}, or {@code null} if the session
* does not exist or is not cached.
*/
protected Session getCachedSession(Serializable sessionId) {
Session cached = null;
if (sessionId != null) {
Cache<Serializable, Session> cache = getActiveSessionsCacheLazy();
if (cache != null) {
cached = getCachedSession(sessionId, cache);
}
}
return cached;
}
/**
* Returns the Session with the specified id from the specified cache. This method simply calls
* {@code cache.get(sessionId)} and can be overridden by subclasses for custom acquisition behavior.
*
* @param sessionId the id of the session to acquire.
* @param cache the cache to acquire the session from
* @return the cached session, or {@code null} if the session wasn't in the cache.
*/
protected Session getCachedSession(Serializable sessionId, Cache<Serializable, Session> cache) {
return cache.get(sessionId);
}
/**
* Caches the specified session under the cache entry key of {@code sessionId}.
*
* @param session the session to cache
* @param sessionId the session id, to be used as the cache entry key.
* @since 1.0
*/
protected void cache(Session session, Serializable sessionId) {
if (session == null || sessionId == null) {
return;
}
Cache<Serializable, Session> cache = getActiveSessionsCacheLazy();
if (cache == null) {
return;
}
cache(session, sessionId, cache);
}
/**
* Caches the specified session in the given cache under the key of {@code sessionId}. This implementation
* simply calls {@code cache.put(sessionId,session)} and can be overridden for custom behavior.
*
* @param session the session to cache
* @param sessionId the id of the session, expected to be the cache key.
* @param cache the cache to store the session
*/
protected void cache(Session session, Serializable sessionId, Cache<Serializable, Session> cache) {
cache.put(sessionId, session);
}
/**
* Attempts to acquire the Session from the cache first using the session ID as the cache key. If no session
* is found, {@code super.readSession(sessionId)} is called to perform the actual retrieval.
*
* @param sessionId the id of the session to retrieve from the EIS.
* @return the session identified by {@code sessionId} in the EIS.
* @throws UnknownSessionException if the id specified does not correspond to any session in the cache or EIS.
*/
public Session readSession(Serializable sessionId) throws UnknownSessionException {
Session s = getCachedSession(sessionId);
if (s == null) {
s = super.readSession(sessionId);
}
return s;
}
/**
* Updates the state of the given session to the EIS by first delegating to
* {@link #doUpdate(org.apache.shiro.session.Session)}. If the session is a {@link ValidatingSession}, it will
* be added to the cache only if it is {@link ValidatingSession#isValid()} and if invalid, will be removed from the
* cache. If it is not a {@code ValidatingSession} instance, it will be added to the cache in any event.
*
* @param session the session object to update in the EIS.
* @throws UnknownSessionException if no existing EIS session record exists with the
* identifier of {@link Session#getId() session.getId()}
*/
public void update(Session session) throws UnknownSessionException {
doUpdate(session);
if (session instanceof ValidatingSession) {
if (((ValidatingSession) session).isValid()) {
cache(session, session.getId());
} else {
uncache(session);
}
} else {
cache(session, session.getId());
}
}
/**
* Subclass implementation hook to actually persist the {@code Session}'s state to the underlying EIS.
*
* @param session the session object whose state will be propagated to the EIS.
*/
protected abstract void doUpdate(Session session);
/**
* Removes the specified session from any cache and then permanently deletes the session from the EIS by
* delegating to {@link #doDelete}.
*
* @param session the session to remove from caches and permanently delete from the EIS.
*/
public void delete(Session session) {
uncache(session);
doDelete(session);
}
/**
* Subclass implementation hook to permanently delete the given Session from the underlying EIS.
*
* @param session the session instance to permanently delete from the EIS.
*/
protected abstract void doDelete(Session session);
/**
* Removes the specified Session from the cache.
*
* @param session the session to remove from the cache.
*/
protected void uncache(Session session) {
if (session == null) {
return;
}
Serializable id = session.getId();
if (id == null) {
return;
}
Cache<Serializable, Session> cache = getActiveSessionsCacheLazy();
if (cache != null) {
cache.remove(id);
}
}
/**
* Returns all active sessions in the system.
* <p/>
* <p>This implementation merely returns the sessions found in the activeSessions cache. Subclass implementations
* may wish to override this method to retrieve them in a different way, perhaps by an RDBMS query or by other
* means.
*
* @return the sessions found in the activeSessions cache.
*/
public Collection<Session> getActiveSessions() {
Cache<Serializable, Session> cache = getActiveSessionsCacheLazy();
if (cache != null) {
return cache.values();
} else {
return Collections.emptySet();
}
}
}
/**
* SessionDAO implementation that relies on an enterprise caching product as the EIS system of record for all sessions.
* It is expected that an injected {@link org.apache.shiro.cache.Cache Cache} or
* {@link org.apache.shiro.cache.CacheManager CacheManager} is backed by an enterprise caching product that can support
* all application sessions and/or provide disk paging for resilient data storage.
* SessionDAO实现依赖于企业缓存产品作为EIS系统来记录所有会话。
* 要求注入的 Cache或 CacheManager对象需要是企业缓存产品支持的。
* 这样可以用来支持所有应用会话,并且(或者)为弹性数据存储提供磁盘分页。
*
* <h2>Production Note</h2>
* This implementation defaults to using an in-memory map-based {@code CacheManager}, which is great for testing but
* will typically not scale for production environments and could easily cause {@code OutOfMemoryException}s. Just
* don't forget to configure<b>*</b> an instance of this class with a production-grade {@code CacheManager} that can
* handle disk paging for large numbers of sessions and you'll be fine.
* 此实现默认使用基于内存映射的 CacheManager,使用ConcurrentHashMap来实现的
* 这非常适合测试,但通常不会针对生产环境进行扩展,并且很容易导致 {@code OutOfMemoryException}。
* 只是不要忘记使用生产级的CacheManager配置这个类的实例,它可以处理大量会话的磁盘分页,你会没事的。
*
* <p/>
* <b>*</b>If you configure Shiro's {@code SecurityManager} instance with such a {@code CacheManager}, it will be
* automatically applied to an instance of this class and you won't need to explicitly set it in configuration.
* 如果您使用这样的 {@code CacheManager}配置Shiro的 {@code SecurityManager}实例,
* 它将自动应用于此类的实例,您无需在配置中明确设置它。
*
* <h3>Implementation Details</h3>
* This implementation relies heavily on the {@link CachingSessionDAO parent class}'s transparent caching behavior for
* all storage operations with the enterprise caching product. Because the parent class uses a {@code Cache} or
* {@code CacheManager} to perform caching, and the cache is considered the system of record, nothing further needs to
* be done for the {@link #doReadSession}, {@link #doUpdate} and {@link #doDelete} method implementations. This class
* implements those methods as required by the parent class, but they essentially do nothing.
* 此实现在很大程度上依赖于父类CachingSessionDAO对企业缓存产品的所有存储操作的透明缓存行为。
* 因为父类使用 Cache 或 CacheManager 来执行缓存,并且缓存被认为是记录系统,所以不需要为doReadSession,doUpdate 和 doDelete方法实现做任何进一步的工作。
* 此类实现父类所需的那些方法,但它们基本上什么都不做。
* @since 1.0
*/
public class EnterpriseCacheSessionDAO extends CachingSessionDAO {
public EnterpriseCacheSessionDAO() {
setCacheManager(new AbstractCacheManager() {
@Override
protected Cache<Serializable, Session> createCache(String name) throws CacheException {
return new MapCache<Serializable, Session>(name, new ConcurrentHashMap<Serializable, Session>());
}
});
}
protected Serializable doCreate(Session session) {
Serializable sessionId = generateSessionId(session);
assignSessionId(session, sessionId);
return sessionId;
}
protected Session doReadSession(Serializable sessionId) {
return null; //should never execute because this implementation relies on parent class to access cache, which
//is where all sessions reside - it is the cache implementation that determines if the
//cache is memory only or disk-persistent, etc.
//这些方法从来不会执行的,因为这个实现依赖于父类来操作缓存
}
protected void doUpdate(Session session) {
//does nothing - parent class persists to cache.
//什么都不做,父类操作存到缓存中
}
protected void doDelete(Session session) {
//does nothing - parent class removes from cache.
//什么都不做,父类操作从缓存中删除
}
}
总结来说
AbstractSessionDAO提供了SessionDAO的基础实现,如生成会话ID等;
MemorySessionDAO直接在内存中进行会话维护;
CachingSessionDAO提供了对开发者透明的会话缓存的功能,需要设置相应的CacheManager即可,用来缓存活动的会话;这里他比MemorySessionDAO多了一个就是会话缓存功能的这个中间层。
在CachingSessionDAO中,他对
1.create(Session session)的实现是,真实的创建session的工作交给了父类super,它只是缓存下来了所创建的session
2.readSession(Serializable sessionId)的时候,先从自己所缓存的session找,如果找不到了,则从父类super中读取sesion
3.update(Session session)的时候,先调用自己的抽象doUpdate(Session session)方法,然后根据情况操作自己的缓存。
protected abstract void doUpdate(Session session);
4.delete(Session session)的时候,先删除自己这边的缓存,然后调用自己的doDelete方法,这个方法是抽象方法
protected abstract void doDelete(Session session);需要子类实现。
EnterpriseCacheSessionDAO提供了会话缓存功能CacheManger默认实现,默认情况下使用ConcurrentHashMap保存所缓存的会话。这里他没有实现Session的会话维护(这个就需要根据企业的会话存储特点来做了,比如存储到redis中),而是使用了缓存来维护这些会话了,所以他就没有实现会话的read,update,delete;
如果我们要将会话维护到一个集群间可以共享的缓存中,比如redis中的时候,我们可以继承CachingSessionDAO,并实现session在redis中的增加,删除,更新,查询。
sessionManager在这两类的sessionDao实现中调用过程如下
1.sessionManager->MemorySessionDao(做会话维护存储,查询,更新,删除)
2.sessionManager->CachingSessionDao(做了一层缓存)->EnterpriseCacheSessionDAO或自己实现是会话Dao(做会话维护存储,查询,更新,删除);该方法需要注意的是,中间的缓存层(CachingSessionDao)的session的缓存时间一定要短于后面维护层存储的session的有效时间,保证后面的维护层中session过期时,cache中的session一定过期