今天有个项目要从单机改成分布式部署,但是安全框架shiro只有单机存储的SessionDao。
于是自己实现了个基于redis存储的SessionDao。
配置文件:
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="${redis.maxIdle}" />
<property name="maxActive" value="${redis.maxActive}" />
<property name="maxWait" value="${redis.maxWait}" />
<property name="testOnBorrow" value="${redis.testOnBorrow}" />
</bean>
<!-- spring data redis -->
<bean id="jedisConnectionFactory"
class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="usePool" value="true"></property>
<property name="hostName" value="${redis.host}" />
<property name="port" value="${redis.port}" />
<property name="password" value="${redis.pass}" />
<property name="timeout" value="${redis.timeout}" />
<property name="database" value="${redis.default.db}"></property>
<constructor-arg index="0" ref="jedisPoolConfig" />
</bean>
<bean name="redisTemplate"
class="org.springframework.data.redis.core.StringRedisTemplate"
lazy-init="false">
<property name="connectionFactory" ref="jedisConnectionFactory" />
</bean>
shiro配置:
<!-- session管理器 -->
<bean id="sessionManager"
class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<!-- 超时时间 -->
<property name="globalSessionTimeout" value="7200000" />
<!-- session存储的实现 -->
<property name="sessionDAO" ref="collectionRedisSessionDao" />
<!-- sessionIdCookie的实现,用于重写覆盖容器默认的JSESSIONID -->
<property name="sessionIdCookie" ref="sharesession" />
<!-- 定时检查失效的session -->
<property name="sessionValidationSchedulerEnabled" value="true" />
</bean>
主要还是引用自己写的sessionDao。
CollectionSessionDao.java
import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
import javax.annotation.PostConstruct;
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 org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
@SuppressWarnings({"rawtypes","unchecked"})
@Component("collectionRedisSessionDao")
@Lazy(false)
public class CollectionRedisSessionDao extends AbstractSessionDAO {
private static Logger logger = LoggerFactory.getLogger(CollectionRedisSessionDao.class);
@Autowired
private RedisTemplate redisTemplate;
@Value("${redis.key.prefix}")
private String redisPrefixKey;
/**
* The Redis key prefix for the sessions
*/
private String keyPrefix = "shiro_redis_session:";
private String getKey(String originalKey) {
return redisPrefixKey + keyPrefix+originalKey;
}
@PostConstruct
public void init(){
logger.info("注入催收的序列/反序列类");
CollectionSerializer<Serializable> collectionSerializer=CollectionSerializer.getInstance();
redisTemplate.setDefaultSerializer(collectionSerializer);
//redisTemplate默认采用的其实是valueSerializer,就算是采用其他ops也一样,这是一个坑。
redisTemplate.setValueSerializer(collectionSerializer);
}
@Override
public void update(Session session) throws UnknownSessionException {
logger.debug("更新seesion,id=[{}]", session.getId().toString());
try {
redisTemplate.opsForValue().set(getKey(session.getId().toString()), session,30,TimeUnit.MINUTES);
} catch (Exception e) {
logger.error(e.getMessage(),e);
}
}
@Override
public void delete(Session session) {
logger.debug("删除seesion,id=[{}]", session.getId().toString());
try {
String key=getKey(session.getId().toString());
redisTemplate.delete(key);
} catch (Exception e) {
logger.error(e.getMessage(),e);
}
}
@Override
public Collection<Session> getActiveSessions() {
logger.info("获取存活的session");
return Collections.emptySet();
}
@Override
protected Serializable doCreate(Session session) {
Serializable sessionId = generateSessionId(session);
assignSessionId(session, sessionId);
logger.debug("创建seesion,id=[{}]", session.getId().toString());
try {
redisTemplate.opsForValue().set(getKey(session.getId().toString()), session,30,TimeUnit.MINUTES);
} catch (Exception e) {
logger.error(e.getMessage(),e);
}
return sessionId;
}
@Override
protected Session doReadSession(Serializable sessionId) {
logger.debug("获取seesion,id=[{}]", sessionId.toString());
Session readSession = null;
try {
readSession=(Session) redisTemplate.opsForValue().get(getKey(sessionId.toString()));
} catch (Exception e) {
logger.error(e.getMessage());
}
return readSession;
}
}
由于redisTemplate自带的序列和反序列化类不能满足需求,于是也要自己写一个
CollectionSerializer.java
import org.apache.commons.lang3.SerializationUtils;
import java.io.Serializable;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
/**
* 自己实现的序列/反序列类
* @author XieZG
* @Date 2017年3月9日下午2:06:52
* @param <T>
*/
public class CollectionSerializer<T extends Serializable> implements RedisSerializer<T>{
private CollectionSerializer(){}
public static volatile CollectionSerializer<Serializable> collectionSerializer=null;
public static CollectionSerializer<Serializable> getInstance(){
if(collectionSerializer==null){
synchronized (CollectionSerializer.class) {
if(collectionSerializer==null){
collectionSerializer=new CollectionSerializer<>();
}
}
}
return collectionSerializer;
}
@Override
public byte[] serialize(T t) throws SerializationException {
return SerializationUtils.serialize(t);
}
@Override
public T deserialize(byte[] bytes) throws SerializationException {
return SerializationUtils.deserialize(bytes);
}
}
以上,完成。