fastjson:差點被幾個漏洞毀了一世英名 01、前世今生

01、前世今生

我是 fastjson,是個地地道道的杭州土著,但我始終懷揣着一顆走向全世界的雄心。這不,我在 GitHub 上的簡介都換成了英文,國際範十足吧?

如果你的英語功底沒有我家老闆 666 的話,我可以簡單地翻譯下(說人話,不裝逼)。

我是阿里巴巴開源的一款 JSON 解析庫,可以將 Java 對象序列化成 JSON 字符串,同時也可以將 JSON 字符串反序列化爲 Java 對象。

我提供了服務器端和安卓客戶端兩種解析工具,性能表現還不錯。

我提供了便捷的方式來進行 Java 對象和 JSON 之間的互轉,toJSONString()方法用來序列化,parseObject()方法用來反序列化。

我允許轉換預先存在的無法修改的對象(只有 class、沒有源代碼)。

對 Java 泛型有着廣泛的支持。

我支持任意複雜的對象(深度的繼承層次)。

2012 年的時候,我就被開源中國評選爲最受歡迎的國產開源軟件之一。時隔多年,我的流行趨勢沒有絲毫減退,在 JSON 領域,我敢說我是 NO 1,因爲我在 GitHub 上的粉絲數已經超過了 22k,沒有任何人敢忽視我這樣的成就。

02、使用指南

在使用我的 API 之前,需要先在 pom.xml 文件中引入我的依賴。

我來寫一個簡單的測試用例,你看一下。

Writer 是一個普通的 Java 類,有兩個字段,分別是 age 和 name,還有它們倆對應的 getter 和 setter 方法。

main()方法中創建了一個 Writer 對象,然後調用我提供的一個靜態方法JSON.toJSONString()來得到 JSON 字符串。

來看一下打印後的結果。

如果想反序列化的話,執行以下的代碼即可。

調用靜態方法 JSON.parseObject(),傳遞兩個參數,一個是 JSON 字符串,一個是對象的類型。

如果想把 JSON 字符串轉成集合的話,需要調用另外一個靜態方法 JSON.parseArray()。

如果沒有特殊要求的話,我敢這麼說,以上 3 個方法就可以覆蓋到你絕大多數的業務場景了。

03、使用註解

有時候,你的 JSON 字符串中的 key 可能與 Java 對象中的字段不匹配,比如大小寫;有時候,你需要指定一些字段序列化但不反序列化;有時候,你需要日期字段顯示成指定的格式。

這些特殊場景,我統統爲你考慮到了,只需要在對應的字段上加上@JSONField註解就可以了。

先來看一下@JSONField註解的定義吧。

name 用來指定字段的名稱,format 用來指定日期格式,serialize 和 deserialize 用來指定是否序列化和反序列化。

我建議在 getter 字段上使用 @JSONField 註解。來看一下測試代碼。

此時的輸出結果如下所示。

JSON 字符串中的 Age 首字母爲大寫,birthday 的格式符合“年月日”的預期,name 字段沒有出現在結果中,說明沒有被序列化。

04、序列化特性

爲了滿足更多個性化的需求,我在 SerializerFeature 類中定義了很多特性,你可以在調用toJSONString()方法的時候進行指定。

PrettyFormat,讓 JSON 格式打印得更漂亮一些

WriteClassName,輸出類名

UseSingleQuotes,key 使用單引號

WriteNullListAsEmpty,List 爲空則輸出 []

WriteNullStringAsEmpty,String 爲空則輸出“”

等等等等,更多新技能,等待你去開鎖。我這裏寫個簡單的 demo 供你參考。

對比一下配置前和配置後的結果。

05、我爲什麼快

衆所周知,把 Java 對象序列化成 JSON 字符串,是不可能使用字符串直接拼接的,因爲這樣性能很差。比字符串拼接更好的辦法就是使用StringBuilder。

StringBuilder 儘管已經很好了,但在性能上還有上升的空間。“自己動手,豐衣足食”,於是我就創造了一個 SerializeWriter 類,專門用來序列化。

SerializeWriter 類中包含了一個char[] buf,每序列化一次,都要做一次分配,但我使用了 ThreadLocal 來進行優化,這樣就能夠有效地減少對象的分配和垃圾回收,從而提升性能。

除此之外,還有很多其他的細節,比如說使用 IdentityHashMap 而不是 HashMap,既可以避免多餘的 equals 操作,又可以避免多線程併發情況下的死循環。

再比如說,使用 asm 技術來避免反射導致的開銷。

我承認,快的同時,也帶來了一些安全性的問題。尤其是 AutoType 的引入,讓黑客有了可乘之機。

在於黑客的反覆較量中,我雖然變得越來越穩重成熟了,但與此同時,讓我的用戶爲此也付出了沉重的代價。

網絡上也出現了很多不和諧的聲音,他們聲稱我是最垃圾的國產開源軟件之一,只不過憑藉着一些投機取巧贏得了國內開發者的信賴。

但更多的是,對我的不離不棄。

溫少幾乎憑一己之力撐起了一個被廣泛使用 JSON 庫,而其他庫幾乎都是靠一整個團隊,就憑這一點,溫少作爲“初心不改的阿里初代開源人”,當之無愧。

出現漏洞並不可怕,可怕的是發現不了漏洞,或者說無法解決掉漏洞。

爲了徹底解決 AutoType 帶來的問題,在 1.2.68 版本中,我引入了 safeMode 的安全模式,無論白名單和黑名單,都不支持 AutoType,這樣就可以徹底地杜絕攻擊。

安全模式下,checkAutoType()方法會直接拋出異常。

06、尾聲

不管前面的路還有多少艱難困苦,也不管還要面對多少風言風語,我都會砥礪前行,爲了國產開源軟件的蓬勃發展,我願意做一個先驅者,也願意做一個持久戰者。

2020 年最後一篇文章了,我們 2021 年加油,點個贊,留住這最後一刻

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章