fastjson是阿里巴巴的开源一个JSON解析库,通常被用于将Java Bean和JSON 字符串之间进行转换。可是fastjson存在漏洞问题,原因一查是跟autotype相关,所以大家想把autotype关闭,那fastjson如何关闭autotype?下面来我们就来给大家讲解一下。
其实在早期的fastjson版本中(v1.2.25 之前),因为AutoType是默认开启的,并且也没有什么限制,可以说是裸着的。从v1.2.25开始,fastjson默认关闭了autotype支持,并且加入了checkAutotype,加入了黑名单+白名单来防御autotype开启的情况。
autotype是什么?
fastjson的主要功能就是将Java Bean序列化成JSON字符串,这样得到字符串之后就可以通过数据库等方式进行持久化了。
但是,fastjson在序列化以及反序列化的过程中并没有使用Java自带的序列化机制,而是自定义了一套机制。
其实,对于JSON框架来说,想要把一个Java对象转换成字符串,可以有两种选择:
1、基于属性2、基于setter/getter
而我们所常用的JSON序列化框架中,FastJson和jackson在把对象序列化成json字符串的时候,是通过遍历出该类中的所有getter方法进行的。Gson并不是这么做的,他是通过反射遍历该类中的所有属性,并把其值序列化成json。
假设我们有以下一个Java类:
classStore
{
private String name;
private Fruit fruit;
public String getName()
{
return name;
}
publicvoidsetName(String name)
{
this.name = name;
}
public Fruit getFruit()
{
return fruit;
}
publicvoidsetFruit(Fruit fruit)
{
this.fruit = fruit;
}
}
interfaceFruit
{}
classAppleimplementsFruit
{
private BigDecimal price; //省略 setter/getter、toString等}当我们要对他进行序列化的时候,fastjson会扫描其中的getter方法,即找到getName和getFruit,这时候就会将name和fruit两个字段的值序列化到JSON字符串中。
那么问题来了,我们上面的定义的Fruit只是一个接口,序列化的时候fastjson能够把属性值正确序列化出来吗?如果可以的话,那么反序列化的时候,fastjson会把这个fruit反序列化成什么类型呢?
我们尝试着验证一下,基于(fastjson v 1.2.68):
Store store = new Store();
store.setName("Hollis");
Apple apple = new Apple();
apple.setPrice(new BigDecimal(0.5));
store.setFruit(apple);
String jsonString = JSON.toJSONString(store);
System.out.println("toJSONString : " + jsonString);以上代码比较简单,我们创建了一个store,为他指定了名称,并且创建了一个Fruit的子类型Apple,然后将这个store使用JSON.toJSONString进行序列化,可以得到以下JSON内容:
toJSONString : {"fruit":{"price":0.5},"name":"Hollis"}那么,这个fruit的类型到底是什么呢,能否反序列化成Apple呢?我们再来执行以下代码:
Store newStore = JSON.parseObject(jsonString, Store.class);
System.out.println("parseObject : " + newStore);
Apple newApple = (Apple) newStore.getFruit();
System.out.println("getFruit : " + newApple);执行结果如下:
toJSONString:
{
"fruit":
{
"price": 0.5
}
, "name": "Hollis"
}
parseObject: Store
{
name = 'Hollis', fruit = {}
}
Exception in thread "main"
java.lang.ClassCastException: com.hollis.lab.fastjson.test.$Proxy0 cannot be cast to com.hollis.lab.fastjson.test.Appleat com.hollis.lab.fastjson.test.FastJsonTest.main(FastJsonTest.java: 26)可以看到,在将store反序列化之后,我们尝试将Fruit转换成Apple,但是抛出了异常,尝试直接转换成Fruit则不会报错,如:
Fruit newFruit = newStore.getFruit();System.out.println("getFruit : " +
newFruit);以上现象,我们知道,当一个类中包含了一个接口(或抽象类)的时候,在使用fastjson进行序列化的时候,会将子类型抹去,只保留接口(抽象类)的类型,使得反序列化时无法拿到原始类型。
那么有什么办法解决这个问题呢,fastjson引入了AutoType,即在序列化的时候,把原始类型记录下来。
使用方法是通过SerializerFeature.WriteClassName进行标记,即将上述代码中的。
String jsonString = JSON.toJSONString(store);
修改成:
String jsonString = JSON.toJSONString(store,SerializerFeature.WriteClassName);
即可,以上代码,输出结果如下:
System.out.println("toJSONString : " + jsonString);
{
"@type": "com.hollis.lab.fastjson.test.Store"
, "fruit":
{
"@type": "com.hollis.lab.fastjson.test.Apple"
, "price": 0.5
}
, "name": "Hollis"
}可以看到,使用SerializerFeature.WriteClassName进行标记后,JSON字符串中多出了一个@type字段,标注了类对应的原始类型,方便在反序列化的时候定位到具体类型
如上,将序列化后的字符串在反序列化,既可以顺利的拿到一个Apple类型,整体输出内容:
toJSONString:
{
"@type": "com.hollis.lab.fastjson.test.Store"
, "fruit":
{
"@type": "com.hollis.lab.fastjson.test.Apple"
, "price": 0.5
}
, "name": "Hollis"
}
parseObject: Store
{
name = 'Hollis', fruit = Apple
{
price = 0.5
}
}
getFruit: Apple
{
price = 0.5
}这就是AutoType,以及fastjson中引入AutoType的原因。
但是,也正是这个特性,因为在功能设计之初在安全方面考虑的不够周全,也给后续fastjson使用者带来了无尽的痛苦。
现在大家应该知道AutoType的含义了吧,其实AutoType不需要关闭,因为在最新版本中,fastjson默认关闭了autotype支持,最后大家如果想要了解更多json相关知识,敬请关注奇Q工具网。
推荐阅读: