记一次fastjson转jackson的生产事故
之前暴露出fastjson存在漏洞,雖然后期fastjson團(tuán)隊(duì)對(duì)于相關(guān)漏洞有修復(fù),但是為了確保服務(wù)和數(shù)據(jù)的安全性,公司還是決定所有項(xiàng)目棄用fastjson,改用jackson。因?yàn)橹绊?xiàng)目很多地方都運(yùn)用了fastjson,改起來(lái)算是一個(gè)大工程了,有些地方?jīng)]注意可能就會(huì)導(dǎo)致出錯(cuò),這不,在換了以后的確遇到了問(wèn)題。
 客戶(hù)需要下單,具體邏輯我就不講了,里面有一個(gè)json字符串轉(zhuǎn)對(duì)象的操作,然后獲取對(duì)象中的data屬性用于后續(xù)操作。用過(guò)fastjson的伙伴都知道可以使用JSON.parseObject(String content)將json字符串轉(zhuǎn)換為對(duì)象:
公司代碼不便公開(kāi),在這里簡(jiǎn)要的進(jìn)行了更改。通過(guò)程序調(diào)試我們可以看到,最終body的值為"testFastjson",為正確的值。
 
 然后在使用jackson替換fastjson,需要實(shí)現(xiàn)同樣的功能,我們使用了jackson提供的new ObjectMapper().readValue(String content, Class valueType)。因?yàn)閷?duì)jackson的用法不是很清楚,然后就直接運(yùn)用了,代碼如下:
同樣debug以后查看轉(zhuǎn)換后的body值,發(fā)現(xiàn)body的值為"“testFastjson”":
 
 大家有沒(méi)有發(fā)現(xiàn)有什么不一樣?沒(méi)錯(cuò),jackson最后竟然的數(shù)據(jù)是帶""的。what?除了問(wèn)題當(dāng)然是需要解決,不僅要解決,還要搞明白這里為什么會(huì)是這個(gè)樣子。本著懷疑的態(tài)度,我debug進(jìn)了jackson相關(guān)方法的readValue源碼進(jìn)行查看:
繼續(xù)進(jìn)去:
public <T> T readValue(String content, JavaType valueType)throws JsonProcessingException, JsonMappingException{_assertNotNull("content", content);try { // since 2.10 remove "impossible" IOException as per [databind#1675]return (T) _readMapAndClose(_jsonFactory.createParser(content), valueType);} catch (JsonProcessingException e) {throw e;} catch (IOException e) { // shouldn't really happen but being declared need tothrow JsonMappingException.fromUnexpectedIOE(e);}}重點(diǎn)來(lái)了,里面調(diào)用的是_readMapAndClose(JsonParser p0, JavaType valueType)方法,我們繼續(xù)進(jìn)去;
protected Object _readMapAndClose(JsonParser p0, JavaType valueType)throws IOException{try (JsonParser p = p0) {Object result;JsonToken t = _initForReading(p, valueType);final DeserializationConfig cfg = getDeserializationConfig();final DeserializationContext ctxt = createDeserializationContext(p, cfg);if (t == JsonToken.VALUE_NULL) {// Ask JsonDeserializer what 'null value' to use:result = _findRootDeserializer(ctxt, valueType).getNullValue(ctxt);} else if (t == JsonToken.END_ARRAY || t == JsonToken.END_OBJECT) {result = null;} else {JsonDeserializer<Object> deser = _findRootDeserializer(ctxt, valueType);if (cfg.useRootWrapping()) {result = _unwrapAndDeserialize(p, ctxt, cfg, valueType, deser);} else {//走這里result = deser.deserialize(p, ctxt);}ctxt.checkUnresolvedObjectId();}if (cfg.isEnabled(DeserializationFeature.FAIL_ON_TRAILING_TOKENS)) {_verifyNoTrailingTokens(p, ctxt, valueType);}return result;}}我們看一下最后的返回結(jié)果:
 
 發(fā)現(xiàn)返回的結(jié)果是一個(gè)LinkedHashMap,其中的key是data,value是"testFastJson":
到這里我們發(fā)現(xiàn)了,如果以O(shè)bjectMapper作為返回類(lèi)的話(huà),它的本質(zhì)是最終轉(zhuǎn)換成鍵值對(duì)的LinkedHashMap,且key是屬性名稱(chēng),值是屬性對(duì)應(yīng)的值但是加上了雙引號(hào),這就是問(wèn)題的本質(zhì)了,然后我們?cè)僖罁?jù)這個(gè)key去獲取值的話(huà),那就肯定是帶引號(hào)的值了:
 
 所以不對(duì),最終導(dǎo)致問(wèn)題的出現(xiàn)。在這里記錄一下以防大家跟我有同樣問(wèn)題的伙伴提個(gè)醒。如果大家想要獲取正確的數(shù)據(jù),如果已經(jīng)知道需要轉(zhuǎn)換的結(jié)構(gòu),大家可以先自定義個(gè)具體的類(lèi)別,將ObjectMapper.class替換成對(duì)應(yīng)的類(lèi)別即可,如果不知道需要具體轉(zhuǎn)換的類(lèi)別,可以在轉(zhuǎn)換完成之后,調(diào)用對(duì)應(yīng)的asText()方法即可獲得正確的值:
我么看一下改完之后獲取的情況:
 
 至此問(wèn)題得到解決。雖然看起來(lái)很簡(jiǎn)單,但是在實(shí)際操作中還是需要注意。在這里雖然只是多了一個(gè)引號(hào),但是如果不及時(shí)發(fā)現(xiàn)處理將會(huì)導(dǎo)致大量訂單的失敗,會(huì)帶來(lái)很大的損失。記錄此次的問(wèn)題過(guò)程,給自己一個(gè)提醒,以后小問(wèn)題也需要好好的對(duì)待。
總結(jié)
以上是生活随笔為你收集整理的记一次fastjson转jackson的生产事故的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
                            
                        - 上一篇: 常用 IO 模型图解介绍
 - 下一篇: MYSQL专题-绝对实用的MYSQL优化