zoukankan      html  css  js  c++  java
  • Springboot2.x+shiro+redis(Lettuce)整合填坑

    主要记录关键和有坑的地方

    前提:

    1、SpringBoot+shiro已经集成完毕,如果没有集成,先查阅之前的Springboot2.0 集成shiro权限管理

    2、redis已经安装完成

    3、redis客户端使用Lettuce,这也是sprinboot2.0后默认的,与jedis的区别,自行百度

    4、json使用springboot默认的

    一、依赖

            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-cache</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
            </dependency>
        //在用使用shiro的情况下集成redis,可以带这个依赖,shiro-redis已经实现了shiro的redis缓存和session管理
        //如果shiro和redis集成但是不交互,可以不引入,可以自定义 <dependency> <groupId>org.crazycake</groupId> <artifactId>shiro-redis</artifactId> <version>3.1.0</version> </dependency>

    连接池:

        <!--连接池,redis依赖-->
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-pool2</artifactId>
            </dependency>

    必须注销:

            <!--与reids缓存冲突-->
            <!--<dependency>-->
                <!--<groupId>org.springframework.boot</groupId>-->
                <!--<artifactId>spring-boot-devtools</artifactId>-->
                <!--<optional>true</optional>-->
            <!--</dependency>-->

    二、Application.yml

    
    
    spring:
    ...省略

    cache:
    redis:
    time-to-live: 60s
    type: redis
    redis:
    host: 127.0.0.1
    port: 6379
    password: 123456@abc.com
    timeout: 10000
    lettuce:
    pool:
    max-idle: 10
    max-active: 10
    min-idle: 5
    max-wait: 10000
    database: 0

    三、redis配置类

    @Configuration
    @EnableCaching //开启Springboot缓存,重要!!!
    public class RedisConfig extends CachingConfigurerSupport {
        @Resource
        private LettuceConnectionFactory lettuceConnectionFactory;
        private Duration timeToLive = Duration.ofSeconds(60); 
    
        @Bean //在没有指定缓存Key的情况下,key生成策略
        public KeyGenerator keyGenerator() {
            return new KeyGenerator() {
                @Override
                public Object generate(Object target, Method method, Object... params) {
                    StringBuffer sb = new StringBuffer();
                    sb.append(target.getClass().getName());
                    sb.append(method.getName());
                    for (Object obj : params) {
                        sb.append(obj.toString());
                    }
                    return sb.toString();
                }
            };
        }
    
        // 缓存管理器 使用Lettuce,和jedis有很大不同
        @Bean
        public CacheManager cacheManager() {
    //关键点,spring cache的注解使用的序列化都从这来,没有这个配置的话使用的jdk自己的序列化,实际上不影响使用,只是打印出来不适合人眼识别 RedisCacheConfiguration config
    = RedisCacheConfiguration.defaultCacheConfig() .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer()))//key序列化方式 .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer()))//value序列化方式 .disableCachingNullValues()
    .entryTtl(timeToLive);//缓存过期时间 RedisCacheManager.RedisCacheManagerBuilder builder
    = RedisCacheManager.RedisCacheManagerBuilder .fromConnectionFactory(lettuceConnectionFactory) .cacheDefaults(config) .transactionAware(); return builder.build(); } /** * RedisTemplate配置 在单独使用redisTemplate的时候 重新定义序列化方式 */ @Bean public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) { // 设置序列化 Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>( Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); // 配置redisTemplate RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>(); redisTemplate.setConnectionFactory(lettuceConnectionFactory); RedisSerializer<?> stringSerializer = new StringRedisSerializer(); redisTemplate.setKeySerializer(stringSerializer);// key序列化 redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);// value序列化 redisTemplate.setHashKeySerializer(stringSerializer);// Hash key序列化 redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);// Hash value序列化 redisTemplate.afterPropertiesSet(); return redisTemplate; } private RedisSerializer<String> keySerializer() { return new StringRedisSerializer(); } private RedisSerializer<Object> valueSerializer() { // 设置序列化 Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>( Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); return jackson2JsonRedisSerializer; //或者使用GenericJackson2JsonRedisSerializer //return new GenericJackson2JsonRedisSerializer(); } }

    在自定义序列化过程,GenericJackson2JsonRedisSerializer和Jackson2JsonRedisSerializer大部分时候表现没有区别,实际上如果对象中有LinkedHashMap时候,后者会出错,这个以前坑了我很久,自我怀疑了很久。

    建议使用GenericJackson2JsonRedisSerializer来序列化。

    GenericJackson2JsonRedisSerializer和Jackson2JsonRedisSerializer都有一个问题,无法反序列化接口的动态代理类,原因应该是动态代理类没有缺省构造函数,对JPA的自定义结果集支持不好,对Page分页支持不好。

    四、在service上使用缓存,具体Springboot的Cache注解百度上很多。

    @CacheConfig(cacheNames = "user")
    public interface UserService {
        @Cacheable(key = "'userName'.concat(#userName)")
        User findByUserName(String userName);   
    }

    五、必须修改Shiro的AuthorizingRealm,这里也是最坑的地方

    public class MyShiroRealm extends AuthorizingRealm {
    
        @Resource
        @Lazy //就是这里,必须延时加载,根本原因是bean实例化的顺序上,shiro的bean必须要先实例化,否则@Cacheable注解无效,理论上可以用@Order控制顺序
        private UserService userService;
    
        //权限信息,包括角色以及权限
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
          略
        }
    
        /*主要是用来进行身份认证的,也就是说验证用户输入的账号和密码是否正确。*/
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
                throws AuthenticationException {
          略
        }
    
    }

    六、实体中如果有java8time,诸如LocalDateTime,redis缓存反序列化的时候会失败,必须在实体中指定json序列化和反序列化的类@JsonDeserialize和@JsonSerialize

        @JsonDeserialize(using = LocalDateTimeDeserializer.class)
        @JsonSerialize(using = LocalDateTimeSerializer.class)
        @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
        //格式化前台页面收到的json时间格式,不指定的话会变成缺省的"yyyy-MM-dd'T'HH:mm:ss"
        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
        private LocalDateTime createTime;//创建时间
        @JsonDeserialize(using = LocalDateDeserializer.class)
        @JsonSerialize(using = LocalDateSerializer.class)
        @DateTimeFormat(pattern = "yyyy-MM-dd")
        @JsonFormat(pattern = "yyyy-MM-dd")
        private LocalDate expiredDate;//过期日期

    暂时就是这些关键点和关键坑,记录,不然肯定忘记。

  • 相关阅读:
    Android在layout xml中使用include完成静态加载
    ImageSwitch图像切换控件
    合并石子大总结
    子矩阵(暴搜(全排列)+DP)
    回路(一笔画问题)
    道路重建(记忆化搜索+贪心)
    【NOIP2013 普及组】车站分级
    UML的基本关联
    Matlab画图-非常具体,非常全面
    面向对象程序设计与面向过程程序设计解析
  • 原文地址:https://www.cnblogs.com/asker009/p/9813932.html
Copyright © 2011-2022 走看看