項目中用到了shiro session會話管理機制,今天來總結一下,如下都是在spring boot框架實現。java
shiro的session管理機制很完善,也是獨立於j2ee容器且不依賴的,因此咱們徹底可使用shiro提供給咱們的session會話管理來實現咱們的業務邏輯,默認提供的sessionDAO是memorySessionDAO,這裏也主要講它的配置和原理。redis
首先來看下如何在spring boot下配置默認的session會話管理:spring
ShiroConfig類:緩存
@Configuration public class ShiroConfig { @Value("${spring.redis.host}") private String host; @Value("${spring.redis.password}") private String password; @Value("${spring.redis.port}") private int port; @Value("${spring.redis.timeout}") private int timeout; @Value("${spring.cache.type}") private String cacheType ; @Value("${server.session-timeout}") private int tomcatTimeout; @Bean public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //設置realm. securityManager.setRealm(userRealm()); // 自定義緩存實現 使用redis if (Constant.CACHE_TYPE_REDIS.equals(cacheType)) { securityManager.setCacheManager(rediscacheManager()); } else { securityManager.setCacheManager(ehCacheManager()); } securityManager.setSessionManager(sessionManager()); return securityManager; } @Bean UserRealm userRealm() { UserRealm userRealm = new UserRealm(); return userRealm; } @Bean public SessionDAO sessionDAO() { return new MemorySessionDAO();//使用默認的MemorySessionDAO } /** * shiro session的管理 */ @Bean public DefaultWebSessionManager sessionManager() { //配置默認的sesssion管理器 DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); sessionManager.setGlobalSessionTimeout(tomcatTimeout * 1000); sessionManager.setSessionDAO(sessionDAO()); Collection<SessionListener> listeners = new ArrayList<SessionListener>(); listeners.add(new BDSessionListener()); sessionManager.setSessionListeners(listeners); return sessionManager; } @Bean public EhCacheManager ehCacheManager() { EhCacheManager em = new EhCacheManager(); em.setCacheManager(cacheManager()); return em; } @Bean("cacheManager2") CacheManager cacheManager(){ return CacheManager.create(); } }
DefaultWebSessionManager是咱們配置管理器的核心類,裏面配置了session的超時時間,sessionDAO和監聽器。tomcat
在上面咱們向spring boot注入了:session
@Bean public SessionDAO sessionDAO() { return new MemorySessionDAO();//使用默認的MemorySessionDAO }
MemorySessionDAO是shiro已經實現的Session的CRUD實現類,包括對session的操做,增刪改讀。框架
下面來看下MemorySessionDAO的源碼實現:ide
public class MemorySessionDAO extends AbstractSessionDAO { private static final Logger log = LoggerFactory.getLogger(MemorySessionDAO.class); private ConcurrentMap<Serializable, Session> sessions = new ConcurrentHashMap(); public MemorySessionDAO() { } protected Serializable doCreate(Session session) { Serializable sessionId = this.generateSessionId(session); this.assignSessionId(session, sessionId); this.storeSession(sessionId, session); return sessionId; } protected Session storeSession(Serializable id, Session session) { if (id == null) { throw new NullPointerException("id argument cannot be null."); } else { return (Session)this.sessions.putIfAbsent(id, session); } } protected Session doReadSession(Serializable sessionId) { return (Session)this.sessions.get(sessionId); } public void update(Session session) throws UnknownSessionException { this.storeSession(session.getId(), session); } public void delete(Session session) { if (session == null) { throw new NullPointerException("session argument cannot be null."); } else { Serializable id = session.getId(); if (id != null) { this.sessions.remove(id); } } } public Collection<Session> getActiveSessions() { Collection<Session> values = this.sessions.values(); return (Collection)(CollectionUtils.isEmpty(values) ? Collections.emptySet() : Collections.unmodifiableCollection(values)); } }
在源碼中咱們能看到MemorySessionDAO繼承了AbstractSessionDAO類,這是一個抽象類,裏面實現了一部分方法,而AbstractSessionDAO又實現了SessionDAO接口,這纔是頂級接口,裏面提供了5個方法:this
public interface SessionDAO { Serializable create(Session var1); Session readSession(Serializable var1) throws UnknownSessionException; void update(Session var1) throws UnknownSessionException; void delete(Session var1); Collection<Session> getActiveSessions(); }
這5個方法也是memorySessionDAO實現的5個方法。 code
能夠看出配置很是簡單,咱們幾乎不須要作任何事情,下面來看看怎麼來使用吧,
以一個SessionService爲例:
@Service public interface SessionService { List<UserOnline> list(); //獲得系統在線用戶 boolean forceLogout(String sessionId); //強制登出 }
裏面有兩個方法,分別是查詢系統當前在線的用戶,以及強制登出某個用戶。
實現以下:
@Service public class SessionServiceImpl implements SessionService { private final SessionDAO sessionDAO; @Autowired public SessionServiceImpl(SessionDAO sessionDAO) { this.sessionDAO = sessionDAO; } @Override public List<UserOnline> list() { List<UserOnline> list = new ArrayList<>(); Collection<Session> sessions = sessionDAO.getActiveSessions(); for (Session session : sessions) { UserOnline userOnline = new UserOnline(); if (session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY) == null) { continue; } else { SimplePrincipalCollection principalCollection = (SimplePrincipalCollection) session .getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY); UserDO userDO = (UserDO) principalCollection.getPrimaryPrincipal(); userOnline.setUsername(userDO.getUsername()); } userOnline.setId((String) session.getId()); userOnline.setHost(session.getHost()); userOnline.setStartTimestamp(session.getStartTimestamp()); userOnline.setLastAccessTime(session.getLastAccessTime()); userOnline.setTimeout(session.getTimeout()); list.add(userOnline); } return list; } @Override public boolean forceLogout(String sessionId) { Session session = sessionDAO.readSession(sessionId); session.setTimeout(0); return true; } }
在代碼中,咱們注入了shiro的SessionDAO,
Collection<Session> sessions = sessionDAO.getActiveSessions(); 能夠獲得當前在線的session集合。
每個session都封裝了用戶的各類信息,
userOnline.setId((String) session.getId()); //獲得session的ID
userOnline.setHost(session.getHost()); //獲得用戶的主機
userOnline.setStartTimestamp(session.getStartTimestamp()); //獲得用戶的登陸時間
userOnline.setLastAccessTime(session.getLastAccessTime());//獲得用戶最後登陸時間
userOnline.setTimeout(session.getTimeout());/獲得過時時間
若是咱們想在強制讓某用戶登出系統,只須要session.setTimeout(0)便可。
是否是以爲很是簡便,若是系統需求不高,咱們徹底可使用shiro爲咱們實現的這個sessionDAO,
而若是系統有定製需求,咱們也能夠實現自定義的sessionDAO,好比咱們能夠將session的CRUD經過redis來實現,一樣的繼承AbstractSessionDAO類,重寫它的方法便可,將session的各類存取修改以redis爲載體,基本的邏輯就是這樣,若是有問題歡迎在評論中留言。