springboot shiro redis 共享遇到的坑

许俊贤
2023-12-01

spring boot整合shiro redis缓存session的网上也是一堆,问题也是千奇百怪,这篇博客汇总了一个完整的实例。本人也是小白一个,如果有什么问题还请各位大佬们多多指教!

  1. 改写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);

 类似资料: