1、前言

最近升级SpringBoot,从2.1.6版本升级到2.2.6版本,发现enableDefaultTyping方法过期过期了。

该方法是指定序列化输入的类型,就是将数据库里的数据安装一定类型存储到redis缓存中。

2、为什么要指定序列化输入类型

2.1、没有指定序列化输入类型

如果注释掉enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL),那存储到redis里的数据将是没有类型的纯json,我们调用redis API获取到数据后,java解析将是一个LinkHashMap类型的key-value的数据结构,我们需要使用的话就要自行解析,这样增加了编程的复杂度。

[{"id":72,"uuid":"c4d7fc52-4096-4c79-81ef-32cb1b87fd28","type":2}]

2.2、指定序列化输入类型

指定enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL)的话,存储到redis里的数据将是有类型的json数据,例如:

["java.util.ArrayList",[{"@class":"com.model.app","id":72,"uuid":"c4d7fc52-4096-4c79-81ef-32cb1b87fd28","type":2}]]

这样java获取到数据后,将会将数据自动转化为java.util.ArrayList和com.model.app,方便直接使用。

3、enableDefaultTyping过期怎么解决

3.1 查找函数接口

查看enableDefaultTyping内部实现

  1.  
    /** @deprecated */
  2.  
    @Deprecated
  3.  
    public ObjectMapper enableDefaultTyping(ObjectMapper.DefaultTyping dti) {
  4.  
    return this.enableDefaultTyping(dti, As.WRAPPER_ARRAY);
  5.  
    }

查看enableDefaultTyping内部实现

  1.  
    /** @deprecated */
  2.  
    @Deprecated
  3.  
    public ObjectMapper enableDefaultTyping(ObjectMapper.DefaultTyping applicability, As includeAs) {
  4.  
    return this.activateDefaultTyping(this.getPolymorphicTypeValidator(), applicability, includeAs);
  5.  
    }

查看activateDefaultTyping内部实现

  1.  
    public ObjectMapper activateDefaultTyping(PolymorphicTypeValidator ptv, ObjectMapper.DefaultTyping applicability, As includeAs) {
  2.  
    if (includeAs == As.EXTERNAL_PROPERTY) {
  3.  
    throw new IllegalArgumentException("Cannot use includeAs of " + includeAs);
  4.  
    } else {
  5.  
    TypeResolverBuilder<?> typer = this._constructDefaultTypeResolverBuilder(applicability, ptv);
  6.  
    typer = typer.init(Id.CLASS, (TypeIdResolver)null);
  7.  
    typer = typer.inclusion(includeAs);
  8.  
    return this.setDefaultTyping(typer);
  9.  
    }
  10.  
    }

这里我们可以直接调用activateDefaultTyping方法了,从而不用调用过期的enableDefaultTyping方法。

再看activateDefaultTyping的参数默认是哪些呢?代码里有这样一个静态初始化:

  1.  
    static {
  2.  
    DEFAULT_BASE = new BaseSettings((ClassIntrospector)null, DEFAULT_ANNOTATION_INTROSPECTOR, (PropertyNamingStrategy)null, TypeFactory.defaultInstance(), (TypeResolverBuilder)null, StdDateFormat.instance, (HandlerInstantiator)null, Locale.getDefault(), (TimeZone)null, Base64Variants.getDefaultVariant(), LaissezFaireSubTypeValidator.instance);
  3.  
    }

3.2 解决

从而我们知道,默认的参数有哪些,

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);

//objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
objectMapper.activateDefaultTyping(

LaissezFaireSubTypeValidator.instance , 
    ObjectMapper.DefaultTyping.NON_FINAL,

JsonTypeInfo.As.WRAPPER_ARRAY);

jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

4 DefaultTyping 类型定义

如果想知道详细的说明,大家google去吧:

  1.  
    public static enum DefaultTyping {
  2.  
    JAVA_LANG_OBJECT,
  3.  
    OBJECT_AND_NON_CONCRETE,
  4.  
    NON_CONCRETE_AND_ARRAYS,
  5.  
    NON_FINAL,
  6.  
    EVERYTHING;
  7.  
    private DefaultTyping() {
  8.  
    }
  9.  
    }

DefaultTyping有四个选项:

JAVA_LANG_OBJECT: 当对象属性类型为Object时生效;

OBJECT_AND_NON_CONCRETE: 当对象属性类型为Object或者非具体类型(抽象类和接口)时生效;

NON_CONCRETE_AND+_ARRAYS: 同上, 另外所有的数组元素的类型都是非具体类型或者对象类型;

NON_FINAL: 对所有非final类型或者非final类型元素的数组。

因此,当开启DefaultTyping后,会开发者在反序列化时指定要还原的类,过程中调用其构造方法setter方法或某些特殊的getter方法,当这些方法中存在一些危险操作时就造成了代码执行。

下面其实可以分别看看这四个值的作用是什么。

4.1、JAVA_LANG_OBJECT

JAVA_LANG_OBJECT :当类里的属性声明为一个Object时,会对该属性进行序列化和反序列化,并且明确规定类名。(当然,这个Object本身也得是一个可被序列化/反序列化的类)。

例如下面的代码,我们给 People 里添加一个 Object object 。

  1.  
    package com.l1nk3r.jackson;
  2.  
    import com.fasterxml.jackson.databind.ObjectMapper;
  3.  
    import java.io.IOException;
  4.  
     
  5.  
    public class JavaLangObject {
  6.  
    public static void main(String args[]) throws IOException {
  7.  
    People p = new People();
  8.  
    p.age = 10;
  9.  
    p.name = "com.l1nk3r.jackson.l1nk3r";
  10.  
    p.object = new l1nk3r();
  11.  
    ObjectMapper mapper = new ObjectMapper();
  12.  
    mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.JAVA_LANG_OBJECT);
  13.  
    String json = mapper.writeValueAsString(p);
  14.  
    System.out.println(json);
  15.  
    //{"age":10,"name":"com.l1nk3r.jackson.l1nk3r","object":["com.l1nk3r.jackson.l1nk3r",{"length":100}]}
  16.  
    People p2 = mapper.readValue(json, People.class);
  17.  
    System.out.println(p2);
  18.  
    //age=10, name=com.l1nk3r.jackson.l1nk3r, com.l1nk3r.jackson.l1nk3r@4566e5bd
  19.  
    }
  20.  
    }
  21.  
    class People {
  22.  
    public int age;
  23.  
    public String name;
  24.  
    public Object object;
  25.  
     
  26.  
    @Override
  27.  
    public String toString() {
  28.  
    return String.format("age=%d, name=%s, %s", age, name, object == null ? "null" : object);
  29.  
    }
  30.  
    }
  31.  
    class l1nk3r {
  32.  
    public int length = 100;
  33.  
    }

4.2、OBJECT_AND_NON_CONCRETE

所以按照上面的描述那么输出的序列化json信息中应该携带了相关的类的信息,而在反序列化的时候自然会进行还原。

OBJECT_AND_NON_CONCRETE :除了上文 提到的特征,当类里有 Interface 、 AbstractClass 时,对其进行序列化和反序列化。(当然,这些类本身需要是合法的、可以被序列化/反序列化的对象)。

例如下面的代码,这次我们添加名为 Sex 的 interface ,发现它被正确序列化、反序列化了,就是这个选项控制的。

  1.  
    package com.l1nk3r.jackson;
  2.  
    import com.fasterxml.jackson.databind.ObjectMapper;
  3.  
    import java.io.IOException;
  4.  
     
  5.  
    public class JavaLangObject {
  6.  
    public static void main(String args[]) throws IOException {
  7.  
    People p = new People();
  8.  
    p.age = 10;
  9.  
    p.name = "com.l1nk3r.jackson.l1nk3r";
  10.  
    p.object = new l1nk3r();
  11.  
    p.sex=new MySex();
  12.  
    ObjectMapper mapper = new ObjectMapper();
  13.  
    mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE);
  14.  
    String json = mapper.writeValueAsString(p);
  15.  
    System.out.println(json);
  16.  
    //{"age":10,"name":"com.l1nk3r.jackson.l1nk3r","object":["com.l1nk3r.jackson.l1nk3r",{"length":100}],"sex":["com.l1nk3r.jackson.MySex",{"sex":0}]}
  17.  
    People p2 = mapper.readValue(json, People.class);
  18.  
    System.out.println(p2);
  19.  
    //age=10, name=com.l1nk3r.jackson.l1nk3r, com.l1nk3r.jackson.l1nk3r@ff5b51f
  20.  
    }
  21.  
    }
  22.  
    class People {
  23.  
    public int age;
  24.  
    public String name;
  25.  
    public Object object;
  26.  
    public Sex sex;
  27.  
     
  28.  
    @Override
  29.  
    public String toString() {
  30.  
    return String.format("age=%d, name=%s, %s", age, name, object == null ? "null" : object);
  31.  
    }
  32.  
    }
  33.  
    class l1nk3r {
  34.  
    public int length = 100;
  35.  
    }
  36.  
    class MySex implements Sex {
  37.  
    int sex;
  38.  
     
  39.  
    @Override
  40.  
    public int getSex() {
  41.  
    return sex;
  42.  
    }
  43.  
     
  44.  
    @Override
  45.  
    public void setSex(int sex) {
  46.  
    this.sex = sex;
  47.  
    }
  48.  
    }
  49.  
     
  50.  
    interface Sex {
  51.  
    public void setSex(int sex);
  52.  
    public int getSex();
  53.  
    }

默认的、无参的 enableDefaultTyping 是 OBJECT_AND_NON_CONCRETE 。

4.3、NON_CONCRETE_AND_ARRAYS

NON_CONCRETE_AND_ARRAYS :除了上文提到的特征,还支持上文全部类型的Array类型。

例如下面的代码,我们的Object里存放l1nk3r的数组。

  1.  
    package com.l1nk3r.jackson;
  2.  
    import com.fasterxml.jackson.databind.ObjectMapper;
  3.  
    import java.io.IOException;
  4.  
     
  5.  
    public class JavaLangObject {
  6.  
    public static void main(String args[]) throws IOException {
  7.  
    People p = new People();
  8.  
    p.age = 10;
  9.  
    p.name = "com.l1nk3r.jackson.l1nk3r";
  10.  
    l1nk3r[] l1nk3rs= new l1nk3r[2];
  11.  
    l1nk3rs[0]=new l1nk3r();
  12.  
    l1nk3rs[1]=new l1nk3r();
  13.  
    p.object = l1nk3rs;
  14.  
    p.sex=new MySex();
  15.  
    ObjectMapper mapper = new ObjectMapper();
  16.  
    mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_CONCRETE_AND_ARRAYS);
  17.  
    String json = mapper.writeValueAsString(p);
  18.  
    System.out.println(json);
  19.  
    //{"age":10,"name":"com.l1nk3r.jackson.l1nk3r","object":["[Lcom.l1nk3r.jackson.l1nk3r;",[{"length":100},{"length":100}]],"sex":["com.l1nk3r.jackson.MySex",{"sex":0}]}
  20.  
    People p2 = mapper.readValue(json, People.class);
  21.  
    System.out.println(p2);
  22.  
    //age=10, name=com.l1nk3r.jackson.l1nk3r, [Lcom.l1nk3r.jackson.l1nk3r;@1e127982
  23.  
    }
  24.  
    }
  25.  
    class People {
  26.  
    public int age;
  27.  
    public String name;
  28.  
    public Object object;
  29.  
    public Sex sex;
  30.  
     
  31.  
    @Override
  32.  
    public String toString() {
  33.  
    return String.format("age=%d, name=%s, %s", age, name, object == null ? "null" : object);
  34.  
    }
  35.  
    }
  36.  
    class l1nk3r {
  37.  
    public int length = 100;
  38.  
    }
  39.  
    class MySex implements Sex {
  40.  
    int sex;
  41.  
     
  42.  
    @Override
  43.  
    public int getSex() {
  44.  
    return sex;
  45.  
    }
  46.  
     
  47.  
    @Override
  48.  
    public void setSex(int sex) {
  49.  
    this.sex = sex;
  50.  
    }
  51.  
    }
  52.  
     
  53.  
    interface Sex {
  54.  
    public void setSex(int sex);
  55.  
     
  56.  
    public int getSex();
  57.  
    }

4.4、NON_FINAL

NON_FINAL :包括上文提到的所有特征,而且包含即将被序列化的类里的全部、非final的属性,也就是相当于整个类、除final外的的属性信息都需要被序列化和反序列化。

例如下面的代码,添加了类型为l1nk3r的变量,非Object也非虚,但也可以被序列化出来。

  1.  
    package com.l1nk3r.jackson;
  2.  
    import com.fasterxml.jackson.databind.ObjectMapper;
  3.  
    import java.io.IOException;
  4.  
     
  5.  
    public class JavaLangObject {
  6.  
    public static void main(String args[]) throws IOException {
  7.  
    People p = new People();
  8.  
    p.age = 10;
  9.  
    p.name = "l1nk3r";
  10.  
    p.object = new l1nk3r();
  11.  
    p.sex=new MySex();
  12.  
    p.l1nk3r=new l1nk3r();
  13.  
    ObjectMapper mapper = new ObjectMapper();
  14.  
    mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
  15.  
    String json = mapper.writeValueAsString(p);
  16.  
    System.out.println(json);
  17.  
    //["com.l1nk3r.jackson.People",{"age":10,"name":"l1nk3r","object":["com.l1nk3r.jackson.l1nk3r",{"length":100}],"sex":["com.l1nk3r.jackson.MySex",{"sex":0}],"l1nk3r":["com.l1nk3r.jackson.l1nk3r",{"length":100}]}]
  18.  
    People p2 = mapper.readValue(json, People.class);
  19.  
    System.out.println(p2);
  20.  
    //age=10, name=l1nk3r, com.l1nk3r.jackson.l1nk3r@ff5b51f
  21.  
    }
  22.  
    }
  23.  
    class People {
  24.  
    public int age;
  25.  
    public String name;
  26.  
    public Object object;
  27.  
    public Sex sex;
  28.  
    public l1nk3r l1nk3r;
  29.  
     
  30.  
    @Override
  31.  
    public String toString() {
  32.  
    return String.format("age=%d, name=%s, %s", age, name, object == null ? "null" : object, sex == null ? "null" : sex,
  33.  
    l1nk3r == null ? "null" : l1nk3r);
  34.  
    }
  35.  
    }
  36.  
    class l1nk3r {
  37.  
    public int length = 100;
  38.  
    }
  39.  
    class MySex implements Sex {
  40.  
    int sex;
  41.  
     
  42.  
    @Override
  43.  
    public int getSex() {
  44.  
    return sex;
  45.  
    }
  46.  
     
  47.  
    @Override
  48.  
    public void setSex(int sex) {
  49.  
    this.sex = sex;
  50.  
    }
  51.  
    }
  52.  
     
  53.  
    interface Sex {
  54.  
    public void setSex(int sex);
  55.  
     
  56.  
    public int getSex();
  57.  
    }

5 JsonTypeInfo注解

@JsonTypeInfo 也是jackson多态类型绑定的一种方式,它一共支持下面5种类型的取值。

  1.  
    @JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
  2.  
    @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
  3.  
    @JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS)
  4.  
    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME)
  5.  
    @JsonTypeInfo(use = JsonTypeInfo.Id.COSTOM)

下面使用一段测试代码可以看看这五个类型的分别作用。

  1.  
    package com.l1nk3r.jackson;
  2.  
    import com.fasterxml.jackson.annotation.JsonTypeInfo;
  3.  
    import com.fasterxml.jackson.databind.ObjectMapper;
  4.  
    import java.io.IOException;
  5.  
     
  6.  
    public class Jsontypeinfo {
  7.  
    public static void main(String[] args) throws IOException {
  8.  
    ObjectMapper mapper= new ObjectMapper();
  9.  
    User user = new User();
  10.  
    user.name= "l1nk3r";
  11.  
    user.age=100;
  12.  
    user.obj=new Height();
  13.  
    String json = mapper.writeValueAsString(user);
  14.  
    System.out.println(json);
  15.  
    }
  16.  
    }
  17.  
     
  18.  
    class User{
  19.  
    public String name;
  20.  
    public int age;
  21.  
    @JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
  22.  
    public Object obj;
  23.  
     
  24.  
    public String toString(){
  25.  
    return "name:" + name + " age:" + age + " obj:" + obj;
  26.  
    }
  27.  
    }
  28.  
     
  29.  
    class Height{
  30.  
    public int h = 100;
  31.  
    }

5.1、Id.NONE

这种方式的输出结果实际上是我们最想要的,这里只需要相关参数的值,并没有其他一些无用信息。

{"name":"l1nk3r","age":100,"obj":{"h":100}}

5.2、Id.CLASS

这种方式的输出结果中携带了相关java类,也就是说反序列化的时候如果使用了JsonTypeInfo.Id.CLASS修饰的话,可以通过 @class 方式指定相关类,并进行相关调用。

{"name":"l1nk3r","age":100,"obj":{"@class":"com.l1nk3r.jackson.Height","h":100}}

5.3、Id.MINIMAL_CLASS

这种方式的输出结果也携带了相关类,和 id.CLASS 的区别在 @class 变成了 @c ,从官方文档中描述中这个应该是一个更短的类名字。同样也就是说反序列化的时候如果使用了JsonTypeInfo.Id.MINIMAL_CLASS修饰的话,可以通过 @c 方式指定相关类,并进行相关调用。

{"name":"l1nk3r","age":100,"obj":{"@c":"com.l1nk3r.jackson.Height","h":100}}

5.4、Id.NAME

这种输出方式没有携带类名字,在反序列化时也是不可以利用的。

{"name":"l1nk3r","age":100,"obj":{"@type":"Height","h":100}}

5.5、Id.COSTOM

这个无法直接用,需要手写一个解析器才可以配合使用,所以直接回抛出异常。

6、参考:

http://www.lmxspace.com/2019/07/30/Jackson-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%B1%87%E6%80%BB/

RedisTemplate配置的jackson.ObjectMapper里的一个enableDefaultTyping方法过期解决的更多相关文章

  1. spring boot定制Jackson ObjectMapper,为什么不生效

    先说结论: 项目中定制了spring 的redisTemplate,而这个template没有使用我自定义的Jackson ObjectMapper.所以不生效. 下面是详细过程: 起因是spring ...

  2. 180611-Spring之RedisTemplate配置与使用

        logo 文章链接:https://liuyueyi.github.io/hexblog/2018/06/11/180611-Spring之RedisTemplate配置与使用/ Spring ...

  3. Maven:如何在eclipse里新建一个Maven的java项目和web项目

    如何在eclipse里新建一个Maven的java项目和web项目: 一:java项目 New-->Other-->Maven 右击项目-->properties,修改以下文件: ① ...

  4. ASP.NET MVC WebApi 返回数据类型序列化控制(json,xml) 用javascript在客户端删除某一个cookie键值对 input点击链接另一个页面,各种操作。 C# 往线程里传参数的方法总结 TCP/IP 协议 用C#+Selenium+ChromeDriver 生成我的咕咚跑步路线地图 (转)值得学习百度开源70+项目

    ASP.NET MVC WebApi 返回数据类型序列化控制(json,xml)   我们都知道在使用WebApi的时候Controller会自动将Action的返回值自动进行各种序列化处理(序列化为 ...

  5. Entity Framework 6 Recipes 2nd Edition(11-6)译 -> 从一个”模型定义”函数里返回一个复杂类型

    11-6.从一个”模型定义”函数里返回一个复杂类型 问题 想要从一个”模型定义”函数返回一个复杂类型 解决方案 假设我们有一个病人(patient)和他们访客(visit)的模型,如 Figure 1 ...

  6. Jackson ObjectMapper类使用解析

    /** * Jackson ObjectMapper类 */ //ObjectMapper类是Jackson库的主要类.它提供一些功能将转换成Java对象匹配JSON结构,反之亦然.它使用JsonPa ...

  7. 第一次使用Android Studio时你应该知道的一切配置(二):新建一个属于自己的工程并安装Genymotion模拟器

    [声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...

  8. try {}里有一个return语句,那么紧跟在这个try后的finally {}里的code会不会被执行,什么时候被执行,还是在return之后执行?

    这是一个很有趣的问题,我测试的结果是:是在return中间执行. 我在网上搜寻了一些资料,下面是参考代码: /** * */ package com.b510.test; /** * try {}里有 ...

  9. jquery-multiselect在ie6里的一个bug

    在使用jquery-multiselect(一个把下拉框改造成带checkbox的可以多选的控件)时,正常时应该是下面这样:而它在ie6里是下面这样: 其中第一个bug参考‘ie6里png图片不透明’ ...

  10. Jackson ObjectMapper类

    ObjectMapper类是Jackson库的主要类.它提供一些功能将转换成Java对象匹配JSON结构,反之亦然.它使用JsonParser和JsonGenerator的实例实现JSON实际的读/写 ...

随机推荐

  1. 数据库故障处理优质文章汇总(含Oracle、MySQL、MogDB等)

    数据库告警.紧急问题处理这些项目对于大多数朋友来讲应该不陌生了.从墨天轮社区整理的"最受DBA欢迎的技术文档合辑"系列中可以发现,大家对"故障诊断专题"的热情居 ...

  2. 云原生周刊:Kubernetes v1.29 新特性一览 | 2023.12.11

    开源项目推荐 kubedog Kubedog 是一个用于在 CI/CD 部署管道中监视和跟踪 Kubernetes 资源的库. 这个库被用于 werf CI/CD 工具中,在部署过程中跟踪资源. Ru ...

  3. AtCoder Beginner Contest 375 C题 (python解)

    Panasonic Programming Contest 2024(AtCoder Beginner Contest 375)C - Spiral Rotation(python解)** 原题链接: ...

  4. Docker容器与守护进程运维 --项目四

    一.Docker容器配置进阶 1.容器的自动重启 Docker提供重启策略控制容器退出时或Docker重启时是否自动启动该容器. 容器默认不支持自动重启,要使用 --restart 选项指定重启策略. ...

  5. [Windows]文件搜索利器Everything(附zip)

    前言 写代码过程中,老大突然发一条信息 老大:这周周报发一下. 我:好的. 然后我就 显示桌面 打开-我的电脑 找到E盘,找到周报文件夹 寻找到所有周报中今天的周报 复制发送 当我用上Everythi ...

  6. 3.10 Linux创建目录(mkdir命令)

    mkdir 命令,是 make directories 的缩写,用于创建新目录,此命令所有用户都可以使用. mkdir 命令的基本格式为: [root@localhost ~]# mkdir [-mp ...

  7. 内核源码+vscode+bear+clang实现函数任意跳转,无缝跳转,无缝阅读,无缝开发

    一.准备工作 1.内核源码版本选择 务必有一份能编译通过的<内核源码>,本次选择5.10版本的. #说明:5.10版本的<内核源码>里,在 scripts/clang-tool ...

  8. J2eefast页面调试状态下自动进入debugger断点

    将fastJS.min.js 替换为 fastJs.js 将所使用到的模块的min.js 替换为js文件

  9. markdown小小白常用语法

    第一次用vscode写笔记去同步Cnblog,不知道写啥就记点常用的md语法吧 1. 标题怎么写? 利用"#" + " " 即可实现第几节标题(其中'/',表转 ...

  10. [python] Python异步编程库asyncio使用指北

    Python的asyncio模块提供了基于协程(coroutines)的异步编程(asynchronous programming)模型.作为一种高效的编程范式,异步编程允许多个轻量级任务并发执行,且 ...