1、配置文件
2、代码结构
3、session共享代码
package com.fangxin365.cms.cluster.session;
import java.io.Serializable;
import java.util.Collection;
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 com.fangxin365.cms.cluster.listener.CustomSessionListener;
import com.fangxin365.cms.cluster.utils.LoggerUtils;
public class CustomShiroSessionDAO extends AbstractSessionDAO {
private static Logger logger = LoggerFactory.getLogger(CustomSessionListener.class);
private ShiroSessionRepository shiroSessionRepository;
@Override
protected Serializable doCreate(Session session) {
logger.info("CustomShiroSessionDAO =====> doCreate");
Serializable sessionId = this.generateSessionId(session);
this.assignSessionId(session, sessionId);
getShiroSessionRepository().saveSession(session);
return sessionId;
}
@Override
public void update(Session session) throws UnknownSessionException {
logger.info("CustomShiroSessionDAO =====> update");
getShiroSessionRepository().saveSession(session);
}
@Override
protected Session doReadSession(Serializable sessionId) {
logger.info("CustomShiroSessionDAO =====> doReadSession");
return getShiroSessionRepository().getSession(sessionId);
}
@Override
public Collection
getActiveSessions() {
logger.info("CustomShiroSessionDAO =====> getActiveSessions");
return getShiroSessionRepository().getAllSessions();
}
@Override
public void delete(Session session) {
logger.info("CustomShiroSessionDAO =====> delete");
if (session == null) {
LoggerUtils.error(getClass(), "Session 不能为null");
return;
}
Serializable id = session.getId();
if (id != null)
getShiroSessionRepository().deleteSession(id);
}
// -----------------------//
// Getter & Setter methods
// -----------------------//
public ShiroSessionRepository getShiroSessionRepository() {
return shiroSessionRepository;
}
public void setShiroSessionRepository(ShiroSessionRepository shiroSessionRepository) {
this.shiroSessionRepository = shiroSessionRepository;
}
}
package com.fangxin365.cms.cluster.session;
import java.io.Serializable;
import java.util.Collection;
import org.apache.shiro.session.Session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fangxin365.cms.cluster.cache.JedisManager;
import com.fangxin365.cms.cluster.session.ShiroSessionRepository;
import com.fangxin365.cms.cluster.utils.LoggerUtils;
import com.fangxin365.cms.cluster.utils.SerializeUtils;
public class JedisShiroSessionRepository implements ShiroSessionRepository {
private static Logger logger = LoggerFactory.getLogger(JedisShiroSessionRepository.class);
public static final String REDIS_SHIRO_SESSION = "fx365-shiro-redis-session:";
//这里有个小BUG,因为Redis使用序列化后,Key反序列化回来发现前面有一段乱码
public static final String REDIS_SHIRO_ALL = "*fx365-shiro-redis-session:*";
private static final int SESSION_VAL_TIME_SPAN = 180000;
private static final int DB_INDEX = 1;
private JedisManager jedisManager;
@Override
public void saveSession(Session session) {
logger.info("JedisShiroSessionRepository =====> saveSession");
if (session == null || session.getId() == null)
throw new NullPointerException("session is empty");
try {
byte[] key = SerializeUtils.serialize(buildRedisSessionKey(session.getId()));
byte[] value = SerializeUtils.serialize(session);
long sessionTimeOut = session.getTimeout() / 1000;
Long expireTime = sessionTimeOut + SESSION_VAL_TIME_SPAN + (5 * 60);
getJedisManager().saveValueByKey(DB_INDEX, key, value, expireTime.intValue());
} catch (Exception e) {
LoggerUtils.fmtError(getClass(), e, "save session error,id:[%s]",session.getId());
}
}
@Override
public Session getSession(Serializable id) {
logger.info("JedisShiroSessionRepository =====> getSession");
if (id == null)
throw new NullPointerException("session id is empty");
Session session = null;
try {
byte[] value = getJedisManager().getValueByKey(DB_INDEX, SerializeUtils.serialize(buildRedisSessionKey(id)));
session = (Session)SerializeUtils.deserialize(value);
} catch (Exception e) {
LoggerUtils.fmtError(getClass(), e, "获取session异常,id:[%s]",id);
}
return session;
}
@Override
public Collection
getAllSessions() {
logger.info("JedisShiroSessionRepository =====> getAllSessions");
Collection
sessions = null;
try {
sessions = getJedisManager().getAllSession(DB_INDEX,REDIS_SHIRO_SESSION);
} catch (Exception e) {
LoggerUtils.fmtError(getClass(), e, "获取全部session异常");
}
return sessions;
}
@Override
public void deleteSession(Serializable id) {
logger.info("JedisShiroSessionRepository =====> deleteSession");
if (id == null) {
throw new NullPointerException("session id is empty");
}
try {
getJedisManager().deleteByKey(DB_INDEX, SerializeUtils.serialize(buildRedisSessionKey(id)));
} catch (Exception e) {
LoggerUtils.fmtError(getClass(), e, "删除session出现异常,id:[%s]",id);
}
}
private String buildRedisSessionKey(Serializable sessionId) {
return REDIS_SHIRO_SESSION + sessionId;
}
// -----------------------//
// Getter & Setter methods
// -----------------------//
public JedisManager getJedisManager() {
return jedisManager;
}
public void setJedisManager(JedisManager jedisManager) {
this.jedisManager = jedisManager;
}
}
package com.fangxin365.cms.cluster.session;
import org.apache.shiro.session.Session;
import java.io.Serializable;
import java.util.Collection;
/**
* Session操作
*/
public interface ShiroSessionRepository {
/**
* 存储Session
*/
void saveSession(Session session);
/**
* 删除session
*/
void deleteSession(Serializable sessionId);
/**
* 获取session
*/
Session getSession(Serializable sessionId);
/**
* 获取所有session
*/
Collection
getAllSessions();
}
4、cache共享代码
package com.fangxin365.cms.cluster.cache.impl;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.util.Destroyable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fangxin365.cms.cluster.cache.ShiroCacheManager;
public class CustomShiroCacheManager implements CacheManager, Destroyable {
private static Logger logger = LoggerFactory.getLogger(CustomShiroCacheManager.class);
private ShiroCacheManager shiroCacheManager;
@Override
public
Cache
getCache(String name) throws CacheException {
logger.info("CustomShiroCacheManager =====> getCache");
return getShiroCacheManager().getCache(name);
}
@Override
public void destroy() throws Exception {
logger.info("CustomShiroCacheManager =====> destroy");
shiroCacheManager.destroy();
}
// -----------------------//
// Getter & Setter methods
// -----------------------//
public ShiroCacheManager getShiroCacheManager() {
return shiroCacheManager;
}
public void setShiroCacheManager(ShiroCacheManager shiroCacheManager) {
this.shiroCacheManager = shiroCacheManager;
}
}
package com.fangxin365.cms.cluster.cache.impl;
import org.apache.shiro.cache.Cache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fangxin365.cms.cluster.cache.JedisManager;
import com.fangxin365.cms.cluster.cache.JedisShiroCache;
import com.fangxin365.cms.cluster.cache.ShiroCacheManager;
public class JedisShiroCacheManager implements ShiroCacheManager {
private static Logger logger = LoggerFactory.getLogger(JedisShiroCacheManager.class);
private JedisManager jedisManager;
@Override
public
Cache
getCache(String name) {
logger.info("JedisShiroCacheManager =====> getCache");
return new JedisShiroCache
(name, getJedisManager());
}
@Override
public void destroy() {
logger.info("JedisShiroCacheManager =====> destroy");
// 如果和其他系统,或者应用在一起就不能关闭
// getJedisManager().getJedis().shutdown();
}
// -----------------------//
// Getter & Setter methods
// -----------------------//
public JedisManager getJedisManager() {
return jedisManager;
}
public void setJedisManager(JedisManager jedisManager) {
this.jedisManager = jedisManager;
}
}
package com.fangxin365.cms.cluster.cache;
import java.util.Collection;
import java.util.Set;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fangxin365.cms.cluster.utils.LoggerUtils;
import com.fangxin365.cms.cluster.utils.SerializeUtils;
@SuppressWarnings("unchecked")
public class JedisShiroCache
implements Cache
{
private static Logger logger = LoggerFactory.getLogger(JedisShiroCache.class);
/**
* 为了不和其他的缓存混淆,采用追加前缀方式以作区分
*/
private static final String REDIS_SHIRO_CACHE = "fx365-shiro-redis-cache:";
private static final int DB_INDEX = 1;
private JedisManager jedisManager;
private String name;
@SuppressWarnings("rawtypes")
static final Class
SELF = JedisShiroCache.class;
public JedisShiroCache(String name, JedisManager jedisManager) {
this.name = name;
this.jedisManager = jedisManager;
}
/**
* 自定义relm中的授权/认证的类名加上授权/认证英文名字
*/
public String getName() {
if (name == null)
return "";
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public V get(K key) throws CacheException {
logger.info("JedisShiroCache =====> get");
byte[] byteKey = SerializeUtils.serialize(buildCacheKey(key));
byte[] byteValue = new byte[0];
try {
byteValue = jedisManager.getValueByKey(DB_INDEX, byteKey);
} catch (Exception e) {
LoggerUtils.error(SELF, "get value by cache throw exception", e);
}
return (V) SerializeUtils.deserialize(byteValue);
}
@Override
public V put(K key, V value) throws CacheException {
logger.info("JedisShiroCache =====> put");
V previos = get(key);
try {
jedisManager.saveValueByKey(DB_INDEX, SerializeUtils.serialize(buildCacheKey(key)), SerializeUtils.serialize(value), -1);
} catch (Exception e) {
LoggerUtils.error(SELF, "put cache throw exception", e);
}
return previos;
}
@Override
public V remove(K key) throws CacheException {
logger.info("JedisShiroCache =====> remove");
V previos = get(key);
try {
jedisManager.deleteByKey(DB_INDEX, SerializeUtils.serialize(buildCacheKey(key)));
} catch (Exception e) {
LoggerUtils.error(SELF, "remove cache throw exception", e);
}
return previos;
}
@Override
public void clear() throws CacheException {
logger.info("JedisShiroCache =====> clear");
}
@Override
public int size() {
logger.info("JedisShiroCache =====> size");
if (keys() == null)
return 0;
return keys().size();
}
@Override
public Set
keys() {
logger.info("JedisShiroCache =====> keys");
return null;
}
@Override
public Collection
values() { logger.info("JedisShiroCache =====> values"); return null; } private String buildCacheKey(Object key) { logger.info("JedisShiroCache =====> buildCacheKey"); return REDIS_SHIRO_CACHE + getName() + ":" + key; } }
package com.fangxin365.cms.cluster.cache;
import org.apache.shiro.cache.Cache;
public interface ShiroCacheManager {
Cache
getCache(String name);
void destroy();
}
5、session监听
package com.fangxin365.cms.cluster.listener;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.SessionListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fangxin365.cms.cluster.session.ShiroSessionRepository;
public class CustomSessionListener implements SessionListener {
private static Logger logger = LoggerFactory.getLogger(CustomSessionListener.class);
private ShiroSessionRepository shiroSessionRepository;
/**
* 一个会话的生命周期开始
*/
@Override
public void onStart(Session session) {
logger.info("CustomSessionListener =====> onStart");
}
/**
* 一个会话的生命周期结束
*/
@Override
public void onStop(Session session) {
logger.info("CustomSessionListener =====> onStop");
}
@Override
public void onExpiration(Session session) {
logger.info("CustomSessionListener =====> onExpiration");
shiroSessionRepository.deleteSession(session.getId());
}
// -----------------------//
// Getter & Setter methods
// -----------------------//
public ShiroSessionRepository getShiroSessionRepository() {
return shiroSessionRepository;
}
public void setShiroSessionRepository(ShiroSessionRepository shiroSessionRepository) {
this.shiroSessionRepository = shiroSessionRepository;
}
}
6、jedis管理
package com.fangxin365.cms.cluster.cache;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.apache.shiro.session.Session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.exceptions.JedisConnectionException;
import com.fangxin365.cms.cluster.session.JedisShiroSessionRepository;
import com.fangxin365.cms.cluster.utils.LoggerUtils;
import com.fangxin365.cms.cluster.utils.SerializeUtils;
public class JedisManager {
private static Logger logger = LoggerFactory.getLogger(JedisManager.class);
private JedisPool jedisPool;
public Jedis getJedis() {
Jedis jedis = null;
try {
jedis = getJedisPool().getResource();
} catch (Exception e) {
throw new JedisConnectionException(e);
}
return jedis;
}
public void returnResource(Jedis jedis, boolean isBroken) {
if (jedis == null)
return;
jedis.close();
}
public void saveValueByKey(int dbIndex, byte[] key, byte[] value, int expireTime) throws Exception {
logger.info("JedisManager =====> saveValueByKey");
Jedis jedis = null;
boolean isBroken = false;
try {
jedis = getJedis();
jedis.select(dbIndex);
jedis.set(key, value);
if (expireTime > 0)
jedis.expire(key, expireTime);
} catch (Exception e) {
isBroken = true;
throw e;
} finally {
returnResource(jedis, isBroken);
}
}
public byte[] getValueByKey(int dbIndex, byte[] key) throws Exception {
logger.info("JedisManager =====> getValueByKey");
Jedis jedis = null;
byte[] result = null;
boolean isBroken = false;
try {
jedis = getJedis();
jedis.select(dbIndex);
result = jedis.get(key);
} catch (Exception e) {
isBroken = true;
throw e;
} finally {
returnResource(jedis, isBroken);
}
return result;
}
/**
* 获取所有Session
*/
public Collection
getAllSession(int dbIndex, String redisShiroSession) throws Exception {
logger.info("JedisManager =====> getAllSession");
Jedis jedis = null;
boolean isBroken = false;
Set
sessions = new HashSet
();
try {
jedis = getJedis();
jedis.select(dbIndex);
Set
byteKeys = jedis.keys((JedisShiroSessionRepository.REDIS_SHIRO_ALL).getBytes());
if (byteKeys != null && byteKeys.size() > 0) {
for (byte[] bs : byteKeys) {
Session obj = (Session)SerializeUtils.deserialize(jedis.get(bs));
if (obj instanceof Session) {
sessions.add(obj);
}
}
}
} catch (Exception e) {
isBroken = true;
throw e;
} finally {
returnResource(jedis, isBroken);
}
return sessions;
}
public void deleteByKey(int dbIndex, byte[] key) throws Exception {
logger.info("JedisManager =====> deleteByKey");
Jedis jedis = null;
boolean isBroken = false;
try {
jedis = getJedis();
jedis.select(dbIndex);
Long result = jedis.del(key);
LoggerUtils.fmtDebug(getClass(), "删除Session结果:%s", result);
} catch (Exception e) {
isBroken = true;
throw e;
} finally {
returnResource(jedis, isBroken);
}
}
// -----------------------//
// Getter & Setter methods
// -----------------------//
public JedisPool getJedisPool() {
return jedisPool;
}
public void setJedisPool(JedisPool jedisPool) {
this.jedisPool = jedisPool;
}
}
7、工具类
package com.fangxin365.cms.cluster.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wjw.kryo.wrapper.KryoSerializer;
public class SerializeUtils {
private static Logger logger = LoggerFactory.getLogger(SerializeUtils.class);
public static Object deserialize(byte[] bytes) {
Object result = null;
if (isEmpty(bytes)) {
return null;
}
try {
result = KryoSerializer.read(bytes);
} catch (Exception e) {
logger.error("Failed to deserialize", e);
}
return result;
}
public static byte[] serialize(Object object) {
byte[] result = null;
if (object == null) {
return new byte[0];
}
try {
result = KryoSerializer.write(object);
} catch (Exception ex) {
logger.error("Failed to serialize", ex);
}
return result;
}
public static boolean isEmpty(byte[] data) {
return (data == null || data.length == 0);
}
}
package com.fangxin365.cms.cluster.utils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
/**
* Log输出封装
*/
public class LoggerUtils {
/**
* 是否开启Debug
*/
public static boolean isDebug = Logger.getLogger(LoggerUtils.class).isDebugEnabled();
/**
* Debug 输出
*
* @param clazz 目标.Class
* @param message 输出信息
*/
public static void debug(Class
clazz, String message) {
if (!isDebug)
return;
Logger logger = Logger.getLogger(clazz);
logger.debug(message);
}
/**
* Debug 输出
*
* @param clazz 目标.Class
* @param fmtString 输出信息key
* @param value 输出信息value
*/
public static void fmtDebug(Class
clazz, String fmtString, Object... value) {
if (!isDebug)
return;
if (StringUtils.isBlank(fmtString)) {
return;
}
if (null != value && value.length != 0) {
fmtString = String.format(fmtString, value);
}
debug(clazz, fmtString);
}
/**
* Error 输出
*
* @param clazz 目标.Class
* @param message 输出信息
* @param e 异常类
*/
public static void error(Class
clazz, String message, Exception e) {
Logger logger = Logger.getLogger(clazz);
if (null == e) {
logger.error(message);
return;
}
logger.error(message, e);
}
/**
* Error 输出
*
* @param clazz 目标.Class
* @param message 输出信息
*/
public static void error(Class
clazz, String message) {
error(clazz, message, null);
}
/**
* 异常填充值输出
*
* @param clazz 目标.Class
* @param fmtString 输出信息key
* @param e 异常类
* @param value 输出信息value
*/
public static void fmtError(Class
clazz, Exception e, String fmtString, Object... value) {
if (StringUtils.isBlank(fmtString)) {
return;
}
if (null != value && value.length != 0) {
fmtString = String.format(fmtString, value);
}
error(clazz, fmtString, e);
}
/**
* 异常填充值输出
*
* @param clazz 目标.Class
* @param fmtString 输出信息key
* @param value 输出信息value
*/
public static void fmtError(Class
clazz, String fmtString, Object... value) {
if (StringUtils.isBlank(fmtString)) {
return;
}
if (null != value && value.length != 0) {
fmtString = String.format(fmtString, value);
}
error(clazz, fmtString);
}
}