spring boot整合shiro redis缓存session的网上也是一堆,问题也是千奇百怪,这篇博客汇总了一个完整的实例。本人也是小白一个,如果有什么问题还请各位大佬们多多指教!
- 改写sessionDao
import com.xsw.common.entity.ShiroSession;
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.AbstractSessionDAO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@Service
public class RedisSessionDAO extends AbstractSessionDAO {
private static Logger logger = LoggerFactory.getLogger(RedisSessionDAO.class);
private RedisTemplate redisTemplate;
private String keyPrefix = "shiro.redis.session:";
public RedisSessionDAO(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
private String getKey(String key) {
return keyPrefix + key;
}
private void saveSession(Session session) throws UnknownSessionException {
if (session != null && session.getId() != null) {
String key = this.getKey(session.getId().toString());
session.setTimeout(30*60*1000);
redisTemplate.opsForValue().set(key, session, 30*60*1000, TimeUnit.MILLISECONDS);
} else {
logger.error("session or session id is null");
}
}
@Override
public void update(Session session) throws UnknownSessionException {
this.saveSession(session);
}
@Override
public void delete(Session session) {
try {
String key = getKey(session.getId().toString());
redisTemplate.delete(key);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
@Override
public Collection<Session> getActiveSessions() {
Set<Session> sessions = new HashSet<>();
Set<String> keys = redisTemplate.keys(getKey("*"));
if (keys != null && keys.size() > 0) {
for (String key : keys) {
Session s = (Session) redisTemplate.opsForValue().get(key);
sessions.add(s);
}
}
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) {
System.err.println("***************************** doReadSession ********************************");
Session readSession = null;
try {
readSession = (Session) redisTemplate.opsForValue().get(getKey(sessionId.toString()));
} catch (Exception e) {
logger.error(e.getMessage(),e);
}
return readSession;
}
2.修改RedisCacheManager
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
public class RedisCacheManager implements CacheManager {
private final ConcurrentMap<String, Cache> caches = new ConcurrentHashMap<String, Cache>();
private RedisTemplate redisTemplate;
public RedisCacheManager(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
@Override
public <K, V> Cache<K, V> getCache(String name) throws CacheException {
Cache cache = this.caches.get(name);
if (cache == null) {
//自定义shiroCache
cache = new ShiroRedisCache<K, V>();
this.caches.put(name, cache);
}
return cache;
}
private class ShiroRedisCache<K, V> implements Cache<K, V> {
private long cacheLive = 30*60*1000;
private String cacheKeyPrefix = "shiro.redis.cacheKey:";
@Override
public V get(K k) throws CacheException {
return (V) redisTemplate.opsForValue().get(this.getRedisCacheKey(k));
}
@Override
public V put(K k, V v) throws CacheException {
redisTemplate.opsForValue().set(this.getRedisCacheKey(k), v, cacheLive, TimeUnit.MINUTES);
return v;
}
@Override
public V remove(K k) throws CacheException {
V obj = (V) redisTemplate.opsForValue().get(this.getRedisCacheKey(k));
redisTemplate.delete(this.getRedisCacheKey(k));
return obj;
}
@Override
public void clear() throws CacheException {
Set keys = redisTemplate.keys(this.cacheKeyPrefix + "*");
if (null != keys && keys.size() > 0) {
Iterator itera = keys.iterator();
redisTemplate.delete(itera.next());
}
}
@Override
public int size() {
Set<K> keys = redisTemplate.keys(this.cacheKeyPrefix + "*");
return keys.size();
}
@Override
public Set<K> keys() {
return redisTemplate.keys(this.cacheKeyPrefix + "*");
}
@Override
public Collection<V> values() {
Set<K> keys = redisTemplate.keys(this.cacheKeyPrefix + "*");
Set<V> values = new HashSet<V>(keys.size());
for (K key : keys) {
values.add((V) redisTemplate.opsForValue().get(this.getRedisCacheKey(key)));
}
return values;
}
private String getRedisCacheKey(K key) {
Object redisKey = this.getStringRedisKey(key);
if (redisKey instanceof String) {
return this.cacheKeyPrefix + redisKey;
} else {
return String.valueOf(redisKey);
}
}
private Object getStringRedisKey(K key) {
Object redisKey;
if (key instanceof PrincipalCollection) {
redisKey = this.getRedisKeyFromPrincipalCollection((PrincipalCollection) key);
} else {
redisKey = key.toString();
}
return redisKey;
}
private Object getRedisKeyFromPrincipalCollection(PrincipalCollection key) {
List realmNames = this.getRealmNames(key);
Collections.sort(realmNames);
Object redisKey = this.joinRealmNames(realmNames);
return redisKey;
}
private List<String> getRealmNames(PrincipalCollection key) {
ArrayList realmArr = new ArrayList();
Set realmNames = key.getRealmNames();
Iterator i$ = realmNames.iterator();
while (i$.hasNext()) {
String realmName = (String) i$.next();
realmArr.add(realmName);
}
return realmArr;
}
private Object joinRealmNames(List<String> realmArr) {
StringBuilder redisKeyBuilder = new StringBuilder();
for (int i = 0; i < realmArr.size(); ++i) {
String s = realmArr.get(i);
redisKeyBuilder.append(s);
}
String redisKey = redisKeyBuilder.toString();
return redisKey;
}
}
最后配置一下shiroConfig 初步实现了redis和shiro之间同步
/**
* shiro session的管理
*/
@Bean
public DefaultWebSessionManager sessionManager() {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setGlobalSessionTimeout(-1000l);
sessionManager.setDeleteInvalidSessions(true);
//实现redis dao
sessionManager.setSessionDAO(redisSessionDAO());
sessionManager.setSessionValidationSchedulerEnabled(true);
sessionManager.setDeleteInvalidSessions(true);
//将修改的cookie放入sessionManager中
sessionManager.setSessionIdCookie(cookieDAO());
Collection<SessionListener> listeners = new ArrayList<SessionListener>();
listeners.add(new BDSessionListener());
sessionManager.setSessionListeners(listeners);
return sessionManager;
}
public Cookie cookieDAO() {
Cookie cookie=new org.apache.shiro.web.servlet.SimpleCookie();
cookie.setName("WEBSID");
return cookie;
}
频繁访问参考大佬的
springboot + shiro 整合 redis 解决频繁访问 redis 和更新 session
我们redis实现之后,开启持久化之后发现其.aof文件是二进制的以及redis缓存均为乱码这时候我们统一配置一下就可以了,新建一个RedisConfig 文件来统一设置
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory factory){
RedisTemplate<String, Integer> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
// 使用Jackson2JsonRedisSerialize 替换默认序列化(默认采用的是JDK序列化)
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
//过滤不必要的字段
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
om.configure(MapperFeature.USE_GETTERS_AS_SETTERS, false);
jackson2JsonRedisSerializer.setObjectMapper(om);
redisTemplate.setKeySerializer(jackson2JsonRedisSerializer);
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashKeySerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
return redisTemplate;
}
}
下面这段代码就是一定要配置,否则在反序列化的时候会出现失败报错,昨天百度找了n遍。另外还出现shiro登录获取实体错误,把项目热部署的代码去除就可以了.
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);