鑑於網上很多關於 shiro做集群后 session共享的回答 但是都有問題 我自己實踐寫一遍記錄
阿新 • • 發佈:2018-12-14
第一個是繼承 AbstractSessionDAO 這個類 這個類是shiro去儲存session的
import com.newcoin.manager.web.utils.ShiroSessionRedisManager; import org.apache.shiro.session.Session; import org.apache.shiro.session.UnknownSessionException; import org.apache.shiro.session.mgt.eis.AbstractSessionDAO; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import java.io.Serializable; import java.util.Collection; import java.util.HashSet; import java.util.Set; public class SessionRedisDao extends AbstractSessionDAO { private Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired private ShiroSessionRedisManager redisManager; /** * The Redis key prefix for the sessions */ private static final String KEY_PREFIX = "shiro_redis_session:"; @Override public void update(Session session) throws UnknownSessionException { this.saveSession(session); } @Override public void delete(Session session) { if (session == null || session.getId() == null) { logger.error("session or session id is null"); return; } redisManager.del(KEY_PREFIX + session.getId()); } /*@Override public Collection<Session> getActiveSessions() { Set<Session> sessions = new HashSet<Session>(); Set<byte[]> keys = redisManager.keys(KEY_PREFIX + "*"); if(keys != null && keys.size()>0){ for(byte[] key : keys){ String deserialize = SerializerUtil.deserialize(key); byte[] o = redisManager.get(deserialize); Session s = (Session) SerializerUtil.deserialize(o); sessions.add(s); } } return sessions; }*/ @Override public Collection<Session> getActiveSessions() { Set<Session> sessions = new HashSet<Session>(); Set<byte[]> keys = redisManager.keys(KEY_PREFIX + "*"); if(keys != null && keys.size()>0){ for(byte[] key : keys){ // String deserialize = SerializerUtil.deserialize(key); Session o = redisManager.get(new String(key)); // Session s = (Session) SerializerUtil.deserialize(o.getBytes()); sessions.add(o); } } return sessions; } @Override protected Serializable doCreate(Session session) { Serializable sessionId = this.generateSessionId(session); this.assignSessionId(session, sessionId); this.saveSession(session); return sessionId; } @Override protected Session doReadSession(Serializable sessionId) { if(sessionId == null){ logger.error("session id is null"); return null; } Session s = (Session)redisManager.get(KEY_PREFIX + sessionId); return s; } private void saveSession(Session session) throws UnknownSessionException { if (session == null || session.getId() == null) { logger.error("session or session id is null"); return; } //設定過期時間 long expireTime = 1800000l; session.setTimeout(expireTime); redisManager.setEx(KEY_PREFIX + session.getId(), session, expireTime); } public void setRedisManager(ShiroSessionRedisManager redisManager) { this.redisManager = redisManager; } public ShiroSessionRedisManager getRedisManager() { return redisManager; } }
以下是2個工具類
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer; /** * 序列化工具類 * @author HandyZcy * */ public class SerializerUtil { private static final JdkSerializationRedisSerializer jdkSerializationRedisSerializer = new JdkSerializationRedisSerializer(); /** * 序列化物件 * @param obj * @return */ public static <T> byte[] serialize(T obj){ try { return jdkSerializationRedisSerializer.serialize(obj); } catch (Exception e) { throw new RuntimeException("序列化失敗!", e); } } /** * 反序列化物件 * @param bytes 位元組陣列 * @return */ @SuppressWarnings("unchecked") public static <T> T deserialize(byte[] bytes){ try { return (T) jdkSerializationRedisSerializer.deserialize(bytes); } catch (Exception e) { throw new RuntimeException("反序列化失敗!", e); } } }
Redis 工具類 具體的RedisTemplate 自己注入
import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.core.RedisCallback; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import java.util.Set; @Component public class ShiroSessionRedisManager { @Autowired private RedisTemplate<Object,Object> redisTemplate; /** * 過期時間 */ // private Long expire; /** * 新增快取資料(給定key已存在,進行覆蓋) * @param key * @param obj * @throws DataAccessException */ public <T> void set(String key, T obj) throws DataAccessException{ final byte[] bkey = key.getBytes(); final byte[] bvalue = SerializerUtil.serialize(obj); redisTemplate.execute(new RedisCallback<Void>() { @Override public Void doInRedis(RedisConnection connection) throws DataAccessException { connection.set(bkey, bvalue); return null; } }); } /** * 新增快取資料(給定key已存在,不進行覆蓋,直接返回false) * @param key * @param obj * @return 操作成功返回true,否則返回false * @throws DataAccessException */ public <T> boolean setNX(String key, T obj) throws DataAccessException{ final byte[] bkey = key.getBytes(); final byte[] bvalue = SerializerUtil.serialize(obj); boolean result = redisTemplate.execute(new RedisCallback<Boolean>() { @Override public Boolean doInRedis(RedisConnection connection) throws DataAccessException { return connection.setNX(bkey, bvalue); } }); return result; } /** * 新增快取資料,設定快取失效時間 * @param key * @param obj * @param expireSeconds 過期時間,單位 秒 * @throws DataAccessException */ public <T> void setEx(String key, T obj, final long expireSeconds) throws DataAccessException{ final byte[] bkey = key.getBytes(); final byte[] bvalue = SerializerUtil.serialize(obj); redisTemplate.execute(new RedisCallback<Boolean>() { @Override public Boolean doInRedis(RedisConnection connection) throws DataAccessException { connection.setEx(bkey, expireSeconds, bvalue); return true; } }); } /** * 獲取key對應value * @param key * @return * @throws DataAccessException */ public <T> T get(final String key) throws DataAccessException{ byte[] result = redisTemplate.execute(new RedisCallback<byte[]>() { @Override public byte[] doInRedis(RedisConnection connection) throws DataAccessException { return connection.get(key.getBytes()); } }); if (result == null) { return null; } return SerializerUtil.deserialize(result); } /** * 刪除指定key資料 * @param key * @return 返回操作影響記錄數 */ public Long del(final String key){ if (StringUtils.isEmpty(key)) { return 0l; } Long delNum = redisTemplate.execute(new RedisCallback<Long>() { @Override public Long doInRedis(RedisConnection connection) throws DataAccessException { byte[] keys = key.getBytes(); return connection.del(keys); } }); return delNum; } public Set<byte[]> keys(final String key){ if (StringUtils.isEmpty(key)) { return null; } Set<byte[]> bytesSet = redisTemplate.execute(new RedisCallback<Set<byte[]>>() { @Override public Set<byte[]> doInRedis(RedisConnection connection) throws DataAccessException { byte[] keys = key.getBytes(); return connection.keys(keys); } }); return bytesSet; } }
然後再shiro的config那裡注入自己寫的sessionDao
@Bean
public SessionRedisDao getRedisSessionDao(){
return new SessionRedisDao();
}
@Bean(name="sessionManager")
public DefaultWebSessionManager defaultWebSessionManager() {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
//sessionManager.setCacheManager(cacheManager());
sessionManager.setGlobalSessionTimeout(43200000); //12小時
sessionManager.setDeleteInvalidSessions(true);
//關鍵在這裡
sessionManager.setSessionDAO(getRedisSessionDao());
sessionManager.setSessionValidationSchedulerEnabled(true);
sessionManager.setDeleteInvalidSessions(true);
// sessionManager.setSessionIdCookie(getSessionIdCookie());
return sessionManager;
}
@Bean(name="securityManager")
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(realm());
// manager.setCacheManager(getRedisManager());
manager.setSessionManager(defaultWebSessionManager());
return manager;
}
萬事大吉
然後在登陸那邊 去進行排擠的程式碼 貼一下
Collection<Session> sessions = sessionRedisDao.getActiveSessions();
String principalsSessionValue = null;
for (Session session : sessions) {
Object attribute = session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
principalsSessionValue = attribute==null?null:attribute.toString();
if(StringUtils.equals(userVo.getAccount(), principalsSessionValue)){
// session.setTimeout(0); //這裡就把session清除
sessionRedisDao.delete(session); //session清除,
}
}