大家好,欢迎来到IT知识分享网。
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内部实现
/ @deprecated */ @Deprecated public ObjectMapper enableDefaultTyping(ObjectMapper.DefaultTyping dti) { return this.enableDefaultTyping(dti, As.WRAPPER_ARRAY); }
查看enableDefaultTyping内部实现
/ @deprecated */ @Deprecated public ObjectMapper enableDefaultTyping(ObjectMapper.DefaultTyping applicability, As includeAs) { return this.activateDefaultTyping(this.getPolymorphicTypeValidator(), applicability, includeAs); }
查看activateDefaultTyping内部实现
public ObjectMapper activateDefaultTyping(PolymorphicTypeValidator ptv, ObjectMapper.DefaultTyping applicability, As includeAs) { if (includeAs == As.EXTERNAL_PROPERTY) { throw new IllegalArgumentException("Cannot use includeAs of " + includeAs); } else { TypeResolverBuilder<?> typer = this._constructDefaultTypeResolverBuilder(applicability, ptv); typer = typer.init(Id.CLASS, (TypeIdResolver)null); typer = typer.inclusion(includeAs); return this.setDefaultTyping(typer); } }
这里我们可以直接调用activateDefaultTyping方法了,从而不用调用过期的enableDefaultTyping方法。
再看activateDefaultTyping的参数默认是哪些呢?代码里有这样一个静态初始化:
static { 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.2 解决
从而我们知道,默认的参数有哪些,
//objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
objectMapper.activateDefaultTyping(
LaissezFaireSubTypeValidator.instance ,
ObjectMapper.DefaultTyping.NON_FINAL,
JsonTypeInfo.As.WRAPPER_ARRAY);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
4 DefaultTyping 类型定义
如果想知道详细的说明,大家google去吧:
public static enum DefaultTyping { JAVA_LANG_OBJECT, OBJECT_AND_NON_CONCRETE, NON_CONCRETE_AND_ARRAYS, NON_FINAL, EVERYTHING; private DefaultTyping() { } }
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 。
package com.l1nk3r.jackson; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; public class JavaLangObject { public static void main(String args[]) throws IOException { People p = new People(); p.age = 10; p.name = "com.l1nk3r.jackson.l1nk3r"; p.object = new l1nk3r(); ObjectMapper mapper = new ObjectMapper(); mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.JAVA_LANG_OBJECT); String json = mapper.writeValueAsString(p); System.out.println(json); //{"age":10,"name":"com.l1nk3r.jackson.l1nk3r","object":["com.l1nk3r.jackson.l1nk3r",{"length":100}]} People p2 = mapper.readValue(json, People.class); System.out.println(p2); //age=10, name=com.l1nk3r.jackson.l1nk3r, com.l1nk3r.jackson.l1nk3r@4566e5bd } } class People { public int age; public String name; public Object object; @Override public String toString() { return String.format("age=%d, name=%s, %s", age, name, object == null ? "null" : object); } } class l1nk3r { public int length = 100; }
4.2、OBJECT_AND_NON_CONCRETE
所以按照上面的描述那么输出的序列化json信息中应该携带了相关的类的信息,而在反序列化的时候自然会进行还原。
OBJECT_AND_NON_CONCRETE :除了上文 提到的特征,当类里有 Interface 、 AbstractClass 时,对其进行序列化和反序列化。(当然,这些类本身需要是合法的、可以被序列化/反序列化的对象)。
例如下面的代码,这次我们添加名为 Sex 的 interface ,发现它被正确序列化、反序列化了,就是这个选项控制的。
package com.l1nk3r.jackson; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; public class JavaLangObject { public static void main(String args[]) throws IOException { People p = new People(); p.age = 10; p.name = "com.l1nk3r.jackson.l1nk3r"; p.object = new l1nk3r(); p.sex=new MySex(); ObjectMapper mapper = new ObjectMapper(); mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE); String json = mapper.writeValueAsString(p); System.out.println(json); //{"age":10,"name":"com.l1nk3r.jackson.l1nk3r","object":["com.l1nk3r.jackson.l1nk3r",{"length":100}],"sex":["com.l1nk3r.jackson.MySex",{"sex":0}]} People p2 = mapper.readValue(json, People.class); System.out.println(p2); //age=10, name=com.l1nk3r.jackson.l1nk3r, com.l1nk3r.jackson.l1nk3r@ff5b51f } } class People { public int age; public String name; public Object object; public Sex sex; @Override public String toString() { return String.format("age=%d, name=%s, %s", age, name, object == null ? "null" : object); } } class l1nk3r { public int length = 100; } class MySex implements Sex { int sex; @Override public int getSex() { return sex; } @Override public void setSex(int sex) { this.sex = sex; } } interface Sex { public void setSex(int sex); public int getSex(); }
默认的、无参的 enableDefaultTyping 是 OBJECT_AND_NON_CONCRETE 。
4.3、NON_CONCRETE_AND_ARRAYS
NON_CONCRETE_AND_ARRAYS :除了上文提到的特征,还支持上文全部类型的Array类型。
例如下面的代码,我们的Object里存放l1nk3r的数组。
package com.l1nk3r.jackson; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; public class JavaLangObject { public static void main(String args[]) throws IOException { People p = new People(); p.age = 10; p.name = "com.l1nk3r.jackson.l1nk3r"; l1nk3r[] l1nk3rs= new l1nk3r[2]; l1nk3rs[0]=new l1nk3r(); l1nk3rs[1]=new l1nk3r(); p.object = l1nk3rs; p.sex=new MySex(); ObjectMapper mapper = new ObjectMapper(); mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_CONCRETE_AND_ARRAYS); String json = mapper.writeValueAsString(p); System.out.println(json); //{"age":10,"name":"com.l1nk3r.jackson.l1nk3r","object":["[Lcom.l1nk3r.jackson.l1nk3r;",[{"length":100},{"length":100}]],"sex":["com.l1nk3r.jackson.MySex",{"sex":0}]} People p2 = mapper.readValue(json, People.class); System.out.println(p2); //age=10, name=com.l1nk3r.jackson.l1nk3r, [Lcom.l1nk3r.jackson.l1nk3r;@1e } } class People { public int age; public String name; public Object object; public Sex sex; @Override public String toString() { return String.format("age=%d, name=%s, %s", age, name, object == null ? "null" : object); } } class l1nk3r { public int length = 100; } class MySex implements Sex { int sex; @Override public int getSex() { return sex; } @Override public void setSex(int sex) { this.sex = sex; } } interface Sex { public void setSex(int sex); public int getSex(); }
4.4、NON_FINAL
NON_FINAL :包括上文提到的所有特征,而且包含即将被序列化的类里的全部、非final的属性,也就是相当于整个类、除final外的的属性信息都需要被序列化和反序列化。
例如下面的代码,添加了类型为l1nk3r的变量,非Object也非虚,但也可以被序列化出来。
package com.l1nk3r.jackson; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; public class JavaLangObject { public static void main(String args[]) throws IOException { People p = new People(); p.age = 10; p.name = "l1nk3r"; p.object = new l1nk3r(); p.sex=new MySex(); p.l1nk3r=new l1nk3r(); ObjectMapper mapper = new ObjectMapper(); mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); String json = mapper.writeValueAsString(p); System.out.println(json); //["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}]}] People p2 = mapper.readValue(json, People.class); System.out.println(p2); //age=10, name=l1nk3r, com.l1nk3r.jackson.l1nk3r@ff5b51f } } class People { public int age; public String name; public Object object; public Sex sex; public l1nk3r l1nk3r; @Override public String toString() { return String.format("age=%d, name=%s, %s", age, name, object == null ? "null" : object, sex == null ? "null" : sex, l1nk3r == null ? "null" : l1nk3r); } } class l1nk3r { public int length = 100; } class MySex implements Sex { int sex; @Override public int getSex() { return sex; } @Override public void setSex(int sex) { this.sex = sex; } } interface Sex { public void setSex(int sex); public int getSex(); }
5 JsonTypeInfo注解
@JsonTypeInfo 也是jackson多态类型绑定的一种方式,它一共支持下面5种类型的取值。
@JsonTypeInfo(use = JsonTypeInfo.Id.NONE) @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) @JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS) @JsonTypeInfo(use = JsonTypeInfo.Id.NAME) @JsonTypeInfo(use = JsonTypeInfo.Id.COSTOM)
下面使用一段测试代码可以看看这五个类型的分别作用。
package com.l1nk3r.jackson; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; public class Jsontypeinfo { public static void main(String[] args) throws IOException { ObjectMapper mapper= new ObjectMapper(); User user = new User(); user.name= "l1nk3r"; user.age=100; user.obj=new Height(); String json = mapper.writeValueAsString(user); System.out.println(json); } } class User{ public String name; public int age; @JsonTypeInfo(use = JsonTypeInfo.Id.NONE) public Object obj; public String toString(){ return "name:" + name + " age:" + age + " obj:" + obj; } } class Height{ public int h = 100; }
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、参考:
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/106654.html
