fastjson的這些坑,你誤入了沒?
背景:最近 fastjson 被爆出新的遠(yuǎn)程代碼執(zhí)行漏洞之后,趕緊督促項(xiàng)目組快馬加鞭去修改(吐槽:真改不動(dòng),架不住項(xiàng)目既多又老),鑒于項(xiàng)目不同,依賴的 fastjson 版本也不同,本次著重談 fastjson 1.2.16 版本遇到的那些問題?1
1
兼容性:低版本沒問題,高版本拋異常?
import com.alibaba.fastjson.JSONArray;import com.alibaba.fastjson.JSONObject;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;/*** fastjson 坑??!** @author 一猿小講*/public class T {public static void main(String[] args) {JSONObject retJson = new JSONObject();retJson.put("retCode", "0000");retJson.put("retMsg", "Pay Succ");ListMapretMap = new HashMap (); retMap.put("orderId", "O010000088888");retMap.put("payerName", "張三");retList.add(retMap);retJson.put("retList", retList);if (retJson.containsKey("retList")) {JSONArray jsonArray = retJson.getJSONArray("retList");for (Object object : jsonArray) {JSONObject orderObject = (JSONObject) object;System.out.println("假裝要執(zhí)行的處理===>" + orderObject);}}}}
com.alibaba fastjson 1.2.16
假裝要執(zhí)行的處理===>{"orderId":"O010000088888","payerName":"張三"}com.alibaba fastjson 1.2.70
Exception in thread "main" java.lang.ClassCastException: java.util.HashMap cannot be cast to com.alibaba.fastjson.JSONObjectat T.main(T.java:31)
JSONObject orderObject = (JSONObject) object;

兩幅圖的標(biāo)注?1 對(duì)比著看,會(huì)發(fā)現(xiàn)低版本的 JSONArray 中存進(jìn)去的是 JSONObject,而高版本的存進(jìn)去的是 HashMap;
兩幅圖的標(biāo)注 2 對(duì)比著看,會(huì)發(fā)現(xiàn)低版本中 JSONObject 強(qiáng)轉(zhuǎn)成 JSONObject,當(dāng)然沒問題,而高版本拿 HashMap 強(qiáng)轉(zhuǎn)成 JSONObject 就會(huì)出現(xiàn) ClassCastException。
版本 1.2.16 的源碼刨到底:

而順著版本 1.2.16 的源碼一路看到底,會(huì)發(fā)現(xiàn)會(huì)對(duì) Map 進(jìn)行二次封裝處理,最終會(huì)包裝成 JSONObject 對(duì)象(這或許就是能強(qiáng)轉(zhuǎn)成 JSONObject 的原因,存進(jìn)去的是牛,強(qiáng)轉(zhuǎn)成牛,當(dāng)然沒問題)。



三、該怎么解決版本升級(jí),帶來的兼容性問題呢?
很簡(jiǎn)單,既然高版本的沒有對(duì) list 中的 Object 進(jìn)行轉(zhuǎn)換,咱們就刻意調(diào)用 toJSON 方法進(jìn)行轉(zhuǎn)換一番。
retJson.put("retList", retList);修改為retJson.put("retList", JSON.toJSON(retList));
代碼跑起來,Debug 看看效果如何:

寫代碼時(shí)候還是需要注意點(diǎn),能稍微規(guī)范些,就盡量按照規(guī)范,就如本次提到的問題,向 JSONObject 中加入 List
時(shí),不妨先提前 toJSON 轉(zhuǎn)換一番,這樣版本升級(jí)也不會(huì)有問題。 由于依賴包升級(jí)導(dǎo)致不兼容的情況很常見,不過絕大多數(shù)都是向下兼容的,例如 JDK ... 5、6、7、8 ...,所以如果你正在開發(fā)核心代碼,若涉及到版本更新,盡量考慮兼容性問題,如果涉及到老功能廢棄時(shí),不妨采用注解標(biāo)注一下,這樣后人會(huì)盡早發(fā)現(xiàn)問題。
2
?不規(guī)范的傳入:導(dǎo)致內(nèi)存溢出??
分享一段有意思的代碼,一起享受其中樂趣。
import?com.alibaba.fastjson.JSON;/**?* fastjson 坑?。?/span>* @author 一猿小講*/public class T {public static void main(String[] args) {String str = "{\"key1\":\"\\value1\"}";Object obj = JSON.parse(str);System.out.println(obj);}}
代碼能跑起來,運(yùn)行結(jié)果絕對(duì)正常
{"key1":"\u000Balue1"}此時(shí),我們動(dòng)點(diǎn)手腳,把 value1 的值變掉
String str = "{\"key1\":\"\\x";程序運(yùn)行,word 天,驚呆!
Exception in thread "main" java.lang.OutOfMemoryError: Java heap spaceat com.alibaba.fastjson.parser.JSONLexerBase.putChar(JSONLexerBase.java:2835)at com.alibaba.fastjson.parser.JSONLexerBase.scanString(JSONLexerBase.java:866)at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:428)at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1302)at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1268)at com.alibaba.fastjson.JSON.parse(JSON.java:137)at com.alibaba.fastjson.JSON.parse(JSON.java:128)at T.main(T.java:11)
好奇不?咋回事?Debug 分析一番就清楚啦。
當(dāng) json 字符串是以?\x 結(jié)尾時(shí),由于低版本的 fastjson 并未對(duì)其進(jìn)行校驗(yàn),將會(huì)導(dǎo)致其繼續(xù)嘗試獲取字符。

由于 index>= this.len 始終成立,則意味著會(huì)直接獲取到?\u001A,相當(dāng)于 26,帶來的結(jié)果就是 isEOF 永遠(yuǎn)為 false,意味著要無休止的讀下去。

就這樣陷入了死循環(huán) 1-->2-->3-->1,直到內(nèi)存溢出。

Debug 一趟肯定比我說的要清楚,當(dāng)然,此問題早已在版本 1.2.60 中修復(fù)。

1.2.70?版本肯定也不會(huì)有此問題啦,在高版本下直接提示格式錯(cuò)誤啦,擺脫了內(nèi)存異常。
Exception in thread "main" com.alibaba.fastjson.JSONException: invalid escape character \xat com.alibaba.fastjson.parser.JSONLexerBase.scanString(JSONLexerBase.java:984)at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:479)at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1401)at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1367)at com.alibaba.fastjson.JSON.parse(JSON.java:183)at com.alibaba.fastjson.JSON.parse(JSON.java:193)at com.alibaba.fastjson.JSON.parse(JSON.java:149)at T.main(T.java:11)
走心:
金無足赤人無完人,代碼有 Bug 很正常,需要一步一步去迭代,要敢于讓團(tuán)隊(duì)犯錯(cuò)、試錯(cuò)、容錯(cuò)。
通過此段測(cè)試,項(xiàng)目開發(fā)中參數(shù)格式校驗(yàn)就很有必要。
3
寄語寫最后
本次,僅以項(xiàng)目中依賴 fastjson 類庫作為切入點(diǎn),主要想傳達(dá):在使用三方輪子時(shí),盡可能做對(duì)三方的輪子了如之掌,知己知彼方能百戰(zhàn)不殆。
另外,借助 fastjson 升級(jí)的事情,也想傳達(dá)寫出規(guī)范性的代碼,是很有必要,不然升級(jí)的時(shí)候就很麻煩。
文末送書
ps:今天開始后面會(huì)有一個(gè)長(zhǎng)期送書計(jì)劃,會(huì)把廣告恰飯的一部分收入回饋給粉絲。
參與形式也都不一樣,本期送書關(guān)鍵字為:0724123456
在后臺(tái)回復(fù)即可以抽獎(jiǎng)了。如圖所示
挑選了3本書籍,中獎(jiǎng)?wù)呖呻S意指定其中一本。
事實(shí)證明:參與抽獎(jiǎng)和中獎(jiǎng)的每次也都是一小部分人

推薦閱讀:
聽說你的JWT庫用起來特別扭,推薦這款賊好用的! 如何實(shí)時(shí)主動(dòng)監(jiān)控你的網(wǎng)站接口是否掛掉并及時(shí)報(bào)警 InnoDB索引允許NULL對(duì)性能有影響嗎
