SpringBoot2(十一)集成RedisCache

Stella981
• 阅读 599

应用层的东西,找到接口实现它即可。

如果想要自己选择序列化工具,难点还是在自动转型上,在序列化字符串转成对象的过程中,Spring并未提供有效的、带Class参数的接口,类型自动转换问题,需要第三方框架自行处理。
最好选用带自动转型的序列化框架,错误的写法,很容易导致类型强转失败,本文采用的是FastJSON。

简单的工具类,按自己需求封装,可以指定各种数据类型序列化格式。

/**
 * @author ChenSS
 * @date    2018年7月13日  v1
 *          2019年10月16日 v2  优化日期
 */
public class FastJsonUtils {
    public static final SerializeConfig serializeConfig;

    static {
        serializeConfig = new SerializeConfig();
        FastJsonDateSerializer dateTimeSerializer = new FastJsonDateSerializer("yyyy-MM-dd HH:mm:ss");
        serializeConfig.put(Date.class, dateTimeSerializer);
        serializeConfig.put(java.sql.Timestamp.class, dateTimeSerializer);
        serializeConfig.put(java.sql.Date.class, new FastJsonDateSerializer("yyyy-MM-dd"));
        serializeConfig.put(java.sql.Time.class, new FastJsonDateSerializer("HH:mm:ss"));

//        // 使用和json-lib兼容的日期输出格式
//        config.put(java.util.Date.class, new JSONLibDataFormatSerializer());
//        config.put(java.sql.Date.class, new JSONLibDataFormatSerializer());
    }
}

FastJson2JsonRedisSerializer

很多人写这个类,完全模仿Jackson的写法,其实没必要,明确自己的需求,保留最少的代码即可。

我泛型直接写Object,因为代码已经能够处理全部类型的数据了。

import cn.seaboot.common.core.FastJsonUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;

import java.nio.charset.Charset;

/**
 * @author Mr.css
 * @date 2020/1/2 11:24
 */
public class FastJson2JsonRedisSerializer implements RedisSerializer<Object> {
  @Override
  public byte[] serialize(Object o) throws SerializationException {
    if (o == null) {
      return new byte[0];
    } else {
      return JSON.toJSONString(o, FastJsonUtils.serializeConfig, SerializerFeature.WriteClassName).getBytes(Charset.defaultCharset());
    }
  }

  @Override
  public Object deserialize(byte[] bytes) throws SerializationException {
    if (bytes == null || bytes.length <= 0) {
      return null;
    } else {
      return JSON.parse(new String(bytes, Charset.defaultCharset()));
    }
  }
}

RedisConfig

如果在使用Cache注解的时候有写key的习惯,KeyGenerator 可以不需要配置,我这里把函数名和所有的参数拼在一起,做成默认的Key值。

import cn.seaboot.common.core.Converter;
import com.alibaba.fastjson.parser.ParserConfig;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.serializer.RedisSerializationContext;

import javax.annotation.Resource;
import java.time.Duration;

/**
 * @author Mr.css on 2019/12/26
 * @date 2019/12/31
 */
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {

  @Resource
  private LettuceConnectionFactory lettuceConnectionFactory;

  /**
   * Cache注解可以不指定key,需要有默认策略,按需调整
   */
  @Bean
  @Override
  public KeyGenerator keyGenerator() {
    return (target, method, params) -> {
      StringBuilder sb = new StringBuilder();
      sb.append(method.getName());
      if(params.length > 0){
        for (int i = 1; i < params.length; i++) {
          sb.append(Converter.toString(params[i]));
        }
      }
      return sb.toString();
    };
  }

  @Bean
  @Override
  public CacheManager cacheManager() {
    RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(lettuceConnectionFactory);

    FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer();
    RedisSerializationContext.SerializationPair<Object> pair =
        RedisSerializationContext.SerializationPair.fromSerializer(serializer);
    RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair);
    //设置过期时间 30天
    defaultCacheConfig = defaultCacheConfig.entryTtl(Duration.ofDays(30));
    //初始化RedisCacheManager
    RedisCacheManager cacheManager = new RedisCacheManager(redisCacheWriter, defaultCacheConfig);
    //反序列化白名单
    ParserConfig.getGlobalInstance().addAccept("cn.seaboot.admin.bean.");
    return cacheManager;
  }
}

yml

  redis:
    host: 127.0.0.1
    port: 6379
    timeout: 1000
    jedis:
      pool:
        min-idle: 1
        max-idle: 8
        max-wait: 5000

Maven

<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>

补充

集成RedisCache,上面代码已经足够,这里介绍FastJSON的一些问题。

FastJSON自动转型的写法

public static void main(String[] args) {
    ParserConfig.getGlobalInstance().addAccept("cn.swsk.xbry.entity");
    TUserInfoEntity entity = new TUserInfoEntity();
    entity.setId("13123");
    //使用 SerializerFeature.WriteClassName 可以让JSON自动转型
    //不依赖 clazz 参数也达到 JSON.parseObject(String json, Class<T> clazz) 相同效果
    String str = JSON.toJSONString(entity, SerializerFeature.WriteClassName);
    System.out.println(JSON.parse(str).getClass());
  }

ParserConfig.getGlobalInstance()的必要性

FastJSON最初是没有白名单这个要求的,addAccept接口的设计源自于系统漏洞。

假设去除掉白名单的设计,在知道全类名的情况下,通过Http接口即可创建出系统的任何对象。

import com.alibaba.fastjson.JSON;
            import com.alibaba.fastjson.parser.ParserConfig;

            /**
             * @author Mr.css
             * @date 2020/1/6
             */
            public class Test {
                public static void main(String[] args) {
                //在没有 ParserConfig.getGlobalInstance() 的情况下,只要知道全类名,即可 new 出程序中任何一个对象
                ParserConfig.getGlobalInstance().addAccept("cn.swsk.xbry.entity");
                //下列这行代码,等效于Class.forName().newInstance()
                System.out.println(JSON.parse("{\"@type\":\"cn.swsk.xbry.entity.TUserInfoEntity\"}").getClass());
                //因为设计原因,或者生产需求,或许你曾经改造过@RequestBody,如果是采用JSON.parse()方式实现的,
                //那么,要是没有白名单的设计,通过http请求即可攻击到系统内部
            }
        }
点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Easter79 Easter79
3年前
SpringBoot自定义序列化的使用方式
场景及需求:项目接入了SpringBoot开发,现在需求是服务端接口返回的字段如果为空,那么自动转为空字符串。例如:\    {        "id":1,        "name":null    },    {        "id":2,        "name":"x
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
3年前
unity将 -u4E00 这种 编码 转汉字 方法
 unity中直接使用 JsonMapper.ToJson(对象),取到的字符串,里面汉字可能是\\u4E00类似这种其实也不用转,服务器会通过类似fastjson发序列化的方式,将json转对象,获取对象的值就是中文但是有时服务器要求将传参中字符串中类似\\u4E00这种转汉字,就需要下面 publ
Stella981 Stella981
3年前
SpringBoot自定义序列化的使用方式
场景及需求:项目接入了SpringBoot开发,现在需求是服务端接口返回的字段如果为空,那么自动转为空字符串。例如:\    {        "id":1,        "name":null    },    {        "id":2,        "name":"x
Easter79 Easter79
3年前
SpringBoot2(十一)集成RedisCache
应用层的东西,找到接口实现它即可。如果想要自己选择序列化工具,难点还是在自动转型上,在序列化字符串转成对象的过程中,Spring并未提供有效的、带Class参数的接口,类型自动转换问题,需要第三方框架自行处理。最好选用带自动转型的序列化框架,错误的写法,很容易导致类型强转失败,本文采用的是FastJSON。简单的工具类,按自己需求封装,可以指定