取代Redistemplate默认的序列化和反序列化方式,同时将项目整体使用的fastjson序列化方式替换成springboot内置的jackson

一、Springboot集成Redis

Maven方式导包

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

自定义RedisTemplate使用

这个包默认提供了Redistemplate和StringRedisTemplate进行操作,使用时直接自动注入即可,内部封装了大量对Redis读取操作,二者的区别基本上就StringRedisTemplate使用了string的序列化方式,从源码就能直接看出来。

public class StringRedisTemplate extends RedisTemplate<String, String> {
    public StringRedisTemplate() {
        this.setKeySerializer(RedisSerializer.string());
        this.setValueSerializer(RedisSerializer.string());
        this.setHashKeySerializer(RedisSerializer.string());
        this.setHashValueSerializer(RedisSerializer.string());
    }

    public StringRedisTemplate(RedisConnectionFactory connectionFactory) {
        this();
        this.setConnectionFactory(connectionFactory);
        this.afterPropertiesSet();
    }

    protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) {
        return new DefaultStringRedisConnection(connection);
    }
}

StringRedisTemplate将key和value的序列化方式都指定为stringRedisSerializer,也是官方推荐我们使用的,但是这东西实际上也有很多坑,而且存储类型有限,每次都需要手动转json字符串,而且在hash类型的value转换这块只能说谁用谁懂
举个简单的例子,因为hash类型的读取是返回一个map,写入是提供一个map存入Redis,然后这里map的序列化和反序列化都是使用的stringRedisSerializer,这里就要求map的key和value均为String类型,那这里就很坑了,当你将对象转成map存储的时候就会一直抛类型转换异常java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String,实际上也就是我对象属性列上有些值是整形或者其他非String类型,转成map之后这些值无法在内部被序列化成String类型,这就是底层采用stringRedisSerializer序列化方式所带来的的深坑

故此,我的建议是一定要选择自己封装RedistTemplate,可定制性强,而且可以自行选择value的序列器,在此我选择的springboot内置的jackson提供的一个序列器,代码如下

@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String,Object>  redisTemplate(RedisConnectionFactory redisConnectionFactory){
        RedisTemplate<String,Object> redisTemplate=new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        

        //创建序列器对象
        GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer=new GenericJackson2JsonRedisSerializer();
        StringRedisSerializer stringRedisSerializer=new StringRedisSerializer();

        //设置string和hash类型key的序列化方式
        redisTemplate.setKeySerializer(stringRedisSerializer);
        redisTemplate.setHashKeySerializer(stringRedisSerializer);

        //设置string和hash类型value序列化的方式
        redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);
        redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer);

        redisTemplate.afterPropertiesSet();

        return redisTemplate;
    }
}

key由于都是String类型,所以直接选择Redis相关包内自带的stringRedisSerializer即可,value采用的jackson提供的genericJackson2JsonRedisSerializer

修改Redis相关配置

    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
    </dependency>

我们需要导入commons-pool2这个包,虽然spring-boot-starter-data-redis包下内置了lettuce,但是其默认是不开启连接池的,池化操作是提升连接性能的一个必需品,Java多线程池,MySQL连接池等等都是通过池化操作来提高性能的,一个高效的连接池能有效提高服务端与Redis服务器交互的性能,lettuce开启的连接池是依赖于commons-pool2这个包的

同时在application.yml中我们需要添加以下内容:

  redis:
    host: your-redis-server-ip
    port: your-port
    password: access-token  #Redis配置中设置的密码 
    database: 0            #数据库索引,默认为0
    connect-timeout: 30s   #连接等待的最大超时时间
    lettuce:               #线程安全,比jedis更适合高并发环境,且是springboot内置默认使用的
      pool:
        max-active: 14    #设置最大连接数,大于Cpu*2,通常为CPU*2+2
        max-idle: 12      #最大空闲连接数 CPU*2
        min-idle: 0      #最小空闲连接数
        max-wait: 5s     #连接池资源耗尽时,任务申请资源最大的阻塞时间,超时将抛出异常JedisConnectionException

二、序列化工具选择

现在工具包很多,很多都提供了json序列化和反序列化的功能,但是其内部实现的不同也造成了质量的参差不齐,很多都存在序列化和反序列化的安全隐患,所以在此我们应该择优选择,而不是单纯以自己用着爽就行来选择

1、fastjson

Github官网的介绍是是这样的:

Fastjson is a Java library that can be used to convert Java Objects
into their JSON representation. It can also be used to convert a JSON
string to an equivalent Java object. Fastjson can work with arbitrary
Java objects including pre-existing objects that you do not have
source-code of.

FASTJSON 2.0.x has been released, faster and more secure, we recommend
you upgrade to the latest version.

我的使用感觉就是简单易用,基本上需要用到的方法都给你封装好了,之前项目里使用的都是fastjson
上面的翻译过来就是fastjson现在推荐都升级到2.0版本,更快也更稳定,1.x版本的坑很多,甚至还出先了反序列化漏洞被远程方法调用从而直接攻击服务器,基本上就和控制了目标服务器差不多,都能在上面随便执行命令控制目标主机了,感兴趣的可以看 fastjson反序列化漏洞
这个漏洞也主要存在于1.x的版本,2.x据说被修复了,但是感觉这个漏洞带来的风险太大,所以国内很多原本选择使用fastjson都选择去寻求替代品了

2、jackson

代码规范,社区活跃,生态系统强大,是springboot内置的序列化工具,在发现fastjson存在很多代码不规范且漏洞频出的情况下,我选择了这个序列化工具来替代项目原有的fastjson的代码块,缺点就是需要自己进行方法的封装,然后有很多配置是需要自己去选择的,但是这种可定制化的情况也给我们提供了很多的便利,需要什么就可以封装成一个方法到工具类中

public class JacksonUtil {
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper()
            .configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true)
            .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false)
            .registerModule(new SimpleModule().addDeserializer(
                    SimpleGrantedAuthority.class, new SimpleGrantedAuthorityDeserializer()));

    /**
     * 将obj对象解析成json字符串
     *
     * @param obj:
     * @return java.lang.String
     */
    public static String toJsonString(Object obj)  {
        if (obj == null) {
            return null;
        }
        try {
            return OBJECT_MAPPER.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 将json字符串解析成Java中集合对象
     * 泛型T代表解析出来的对象类型
     *
     * @param json:
     * @param tTypeReference:里面的泛型是复杂对象的类型,list,map等必须通过这个对象传入
     * @return T
     */
    public static <T> T parseObject(String json, TypeReference<T> tTypeReference)  {
        //对字符串进行专门判空
        if (!StringUtils.hasText(json)) {
            return null;
        }
        if (tTypeReference == null) {
            return null;
        }
        try {
            return OBJECT_MAPPER.readValue(json, tTypeReference);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 获取ObjectMapper
     *
     * @return com.fasterxml.jackson.databind.ObjectMapper
     */
    public static ObjectMapper getObjectMapper() {
        return OBJECT_MAPPER;
    }

    /**
     * 构建ObjectNode
     *
     * @return com.fasterxml.jackson.databind.node.ObjectNode
     */
    public static ObjectNode createObjectNode() {
        return OBJECT_MAPPER.createObjectNode();
    }


    /*
    public static Map beanToMap(Object object)  {
        Map<String, Object> map = new HashMap<String, Object>();
        //利用反射获取对象中所有的属性名和对应值
        Field[] fields = object.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            try {
                map.put(field.getName(), field.get(object));
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return map;
    }
    */

    /**
     * bean转map
     * @param object
     * @return
     */
    public static Map beanToMap(Object object)  {
        Map<String, Object> map = new HashMap<String, Object>();
        if(object!=null){
            BeanMap beanMap=BeanMap.create(object);
            Set<String> keys = beanMap.keySet();
            for (String key : keys) {
                map.put(key,beanMap.get(key))
;            }
        }
        return map;
    }
    /**
     * 将map集合转成Javabean对象
     * @param map
     * @param clazz:Javabean的类对象
     * @param <T>
     * @return
     * @throws JsonProcessingException
     */
    public static <T> T mapToBean(Map<String, Object> map, Class<T> clazz)  {
       String mapString= toJsonString(map);
       return parseObject(mapString,clazz);
    }

    /**
     * 将json字符串解析成JavaBean类型的对象,需要传入对象的Class类型
     *
     * @param json
     * @param bean
     * @param <T>
     * @return
     * @throws JsonProcessingException
     */
    public static <T> T parseObject(String json, Class<T> bean)  {
        if (!StringUtils.hasText(json)) {
            return null;
        }
        try {
            return OBJECT_MAPPER.readValue(json, bean);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            return null;
        }
    }


}

然后再提一嘴,springboot中内置的工具真的很多而且好用,规范安全又易用,比如说这个StringUtils,我一开始使用的common-lang3下的,后面又换成了hutool包下的,最后发现spring内置的这个功能全而且好用,不需要额外导包给自己的项目增加复杂度和维护难度

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot使用fastjson 2.0.0版本时,可以通过配置文件或代码方式来设置反序列化配置。以下是常见的配置方式: 1. 配置文件方式 在application.properties或application.yml文件中添加如下内容: ``` # 开启AutoType功能 spring.fastjson.parser.autoTypeSupport=true # 关闭ASM功能 spring.fastjson.parser.asmEnable=false ``` 2. 代码方式Spring Boot的启动类中,可以通过重写configureMessageConverters方法来设置fastjson反序列化配置,例如: ``` @Configuration public class FastJsonConfig extends WebMvcConfigurationSupport { @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter(); FastJsonConfig config = new FastJsonConfig(); // 开启AutoType config.setAutoTypeSupport(true); // 关闭ASM config.setASMEnable(false); converter.setFastJsonConfig(config); converters.add(converter); } } ``` 注意,Spring Boot默认使用Jackson作为JSON处理库,如果想要使用fastjson,则需要排除Jackson并添加fastjson依赖,例如: ``` <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>2.0.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-json</artifactId> </exclusion> </exclusions> </dependency> ``` 以上是一些常见的反序列化配置方式,具体的配置应该根据具体情况进行选择和设置,以确保安全和性能。建议仔细阅读fastjson的官方文档,并参考相关的安全规范和最佳实践。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值