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工具网。
推荐阅读: