fastjson如何关闭autotype?autotype是什么?

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工具网。

推荐阅读:

JAVA数据库怎么分模块?JAVA数据库分库实例

java线程怎么创建?java线程创建方式

java内存溢出怎么解决?java内存为什么会溢出?