package com.zhuxs.result.shiro; import com.zhuxs.result.utils.SerializeUtils; import org.apache.commons.lang3.StringUtils; import org.apache.shiro.session.Session; import org.apache.shiro.session.UnknownSessionException; import org.apache.shiro.session.mgt.ValidatingSession; import org.apache.shiro.session.mgt.eis.CachingSessionDAO; import org.apache.shiro.subject.support.DefaultSubjectContext; import org.apache.shiro.util.CollectionUtils; import org.joda.time.DateTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.Transaction; import java.io.Serializable; import java.util.Collection; import java.util.List; import java.util.Set; /** * Created by shusesshou on 2017/9/22. * 针对自定义的ShiroSession的Redis CRUD操作,通过isChanged标识符,确定是否需要Update方法 * 通过配置securityManager在属性cacheManager从缓存中查找Session是否存在,如果找不到再调用方法 * Shiro内部相应的组件(DefaultSecurityManager)会自动检测相应的对象(Realm)是否实现了CacheManagerAware并注入相应的CacheManager */ @Service public class ShiroSessionDao extends CachingSessionDAO{ private static final Logger logger = LoggerFactory.getLogger(ShiroSessionDao.class); //保存到Redis中key的前缀 private String prefix = ""; //设置会话的过期时间 private int expireTime = 3600000; //特殊配置 只用于没有redis时,将session放到EhCache中 private Boolean onlyEhcache; @Autowired private JedisPool jedisPool; /** * 如果session中没有登录信息就调用doReadSession方法从Redis中重读 * session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY) == null 代表没有登录,登录后Shiro会放入该值 * @param sessionId * @return * @throws UnknownSessionException */ @Override public Session readSession(Serializable sessionId) throws UnknownSessionException{ Session session = getCachedSession(sessionId); if(session == null || session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY) == null) { session = this.doReadSession(sessionId); if(session == null){ throw new UnknownSessionException("There is no session with id [" + sessionId + "]"); }else { cache(session,session.getId()); } } return session; } /** * 更新会话 * @param session */ @Override protected void doUpdate(Session session) { try { if(session instanceof ValidatingSession && !((ValidatingSession)session).isValid()){ return; } } catch (Exception e){ logger.error("ValidatingSession error"); } try { if(session instanceof ShiroSession){ //如果没有字段(除lastAccessTime以外其他字段)发生改变 ShiroSession shiroSession = (ShiroSession) session; if(!shiroSession.getIsChanged()){ return; } Jedis jedis = null; Transaction transaction = null; try { jedis = jedisPool.getResource(); //开启事务 transaction = jedis.multi(); shiroSession.setIsChanged(false); shiroSession.setLastAccessTime(DateTime.now().toDate()); transaction.setex(prefix + session.getId(),expireTime,SerializeUtils.serializaToString(shiroSession)); logger.debug("sessionId {} name {} 被更新", session.getId(), session.getClass().getName()); //执行事务 transaction.exec(); } catch (Exception e){ if(transaction != null){ transaction.discard(); } throw e; } finally { jedis.close(); } }else { logger.debug("sessionId {} name {} 更新失败", session.getId(), session.getClass().getName()); } } catch (Exception e){ logger.warn("更新Session失败", e); } } /** * 删除会话 * 当会话过期/会话停止时会调用 * @param session */ @Override protected void doDelete(Session session) { logger.debug("begin doDelete {}", session); Jedis jedis = null; try { jedis = jedisPool.getResource(); jedis.del(prefix + session.getId()); this.uncache(session.getId()); logger.debug("shiro session id {} 被删除", session.getId()); } catch (Exception e){ logger.warn("删除session失败",e); } finally { jedis.close(); } } /** * 删除cache中缓存的Session */ public void uncache(Serializable sessionId) { try { Session session = super.getCachedSession(sessionId); super.uncache(session); logger.debug("shiro session id {} 的缓存失效", sessionId); } catch (Exception e) { e.printStackTrace(); } } /** * SessionManager创建完session后会调用该方法 * @param session * @return */ @Override protected Serializable doCreate(Session session) { Serializable sessionId = this.generateSessionId(session); assignSessionId(session,sessionId); Jedis jedis = null; try{ jedis = jedisPool.getResource(); //session由Redis缓存失效决定,这里作简单标识 session.setTimeout(expireTime); jedis.setex(prefix + sessionId, expireTime, SerializeUtils.serializaToString((ShiroSession) session)); logger.info("sessionId {} name {} 被创建", sessionId, session.getClass().getName()); }catch (Exception e){ logger.warn("创建session失败",e); }finally { jedis.close(); } return sessionId; } @Override protected Session doReadSession(Serializable sessionId) { logger.debug("begin doReadSession {}", sessionId); Jedis jedis = jedisPool.getResource(); Session session = null; try { String key = prefix + sessionId; String value = jedis.get(key); if(StringUtils.isNotBlank(value)){ session = SerializeUtils.deserializeFromString(value); logger.info("sessionId {} ttl {}: ", sessionId, jedis.ttl(key)); //重置Redis中缓存过期的时间 jedis.expire(key,expireTime); logger.info("sessionId {} name {} 被读取", sessionId, session.getClass().getName()); } } catch (Exception e){ logger.warn("读取session失败"); } finally { jedis.close(); } return session; } /** * 从Redis中读取,但不重置Redis中缓存过期时间 */ public Session doReadSessionWithoutExpire(Serializable sessionId) { Session session = null; Jedis jedis = null; try { jedis = jedisPool.getResource(); String key = prefix + sessionId; String value = jedis.get(key); if(StringUtils.isNotBlank(value)){ session = SerializeUtils.deserializeFromString(value); } } catch (Exception e){ logger.warn("读取Session失败", e); } finally { jedis.close(); } return session; } /** * 获取当前所有活跃用户 */ @Override public Collection<Session> getActiveSessions(){ Jedis jedis = null; try { jedis = jedisPool.getResource(); Set<String> keys = jedis.keys(prefix + "*"); if(CollectionUtils.isEmpty(keys)){ return null; } List<String> values = jedis.mget(keys.toArray(new String[keys.size()])); return SerializeUtils.deserializeFromStrings(values); } catch (Exception e){ logger.warn("统计Session信息失败", e); } finally { jedis.close(); } return null; } /** * 返回本机Ehcache中Session */ public Collection<Session> getEhCacheActiveSessions() { return super.getActiveSessions(); } public void setPrefix(String prefix) { this.prefix = prefix; } public String getPrefix() { return prefix; } public int getExpireTime() { return expireTime; } public void setExpireTime(int expireTime) { this.expireTime = expireTime; } }