最近項目里用到了阿里巴巴的fastjson工具,遇到一些問題,記錄分享一下
github說明:
fastjson是阿里巴巴的開源JSON解析庫,它可以解析JSON格式的字符串,支持將Java Bean序列化為JSON字符串,也可以從JSON字符串反序列化到JavaBean。
使用:
添加maven依賴
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.54</version>
</dependency>
API使用
String text = JSON.toJSONString(obj); //序列化
VO vo = JSON.parseObject("{...}", VO.class); //反序列化
但有時候會出現
使用Redis 配置替換fastjson 反序列化報錯 com.alibaba.fastjson.JSONException: autoType is not support
解決方法是
在RedisSerializer<T>的實現類中添加白名單
//添加白名單
static {
ParserConfig.getGlobalInstance().addAccept("com.glz.oauthmanager");
}
public class FastJsonRedisSerializer<T> implements RedisSerializer<T> {
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
private Class<T> clazz;
//添加白名單
static {
ParserConfig.getGlobalInstance().addAccept("com.glz.oauthmanager");
}
public FastJsonRedisSerializer(Class<T> clazz) {
super();
this.clazz = clazz;
}
@Override
public byte[] serialize(T t) throws SerializationException {
if (t == null) {
return new byte[0];
}
return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
}
@Override
public T deserialize(byte[] bytes) throws SerializationException {
if (bytes == null || bytes.length <= 0) {
return null;
}
String str = new String(bytes, DEFAULT_CHARSET);
return (T) JSON.parseObject(str, clazz);
}
}
如果還是不行,官方還有其他解決辦法
逆風的方向,更適合飛翔!
https://www.cnblogs.com/nisiweiLIQIYONG/p/10843581.html
由於項目使用的是fastjson,也無法換成其他的序列化框架,所以研究了一下他對泛型序列化和反序列化的支持能力,最終解決了這個問題。
要達成的目標
我的封裝方式屬於通用封裝,我要達到的目標是如下的使用方式:
放入數據:
Map<String, OffheapDTO> mapxx = new HashMap<>();
mapxx.put("1",new OffheapDTO().create()); mapxx.put("2",new OffheapDTO().create()); mapxx.put("3",new OffheapDTO().create()); mapxx.put("4",new OffheapDTO().create()); mapxx.put("5",new OffheapDTO().create()); offheapCacheWrapper.putMap("maptest", mapxx);
獲取數據:
Map<String, OffheapDTO> resultmap = offheapCacheWrapper.queryMap("cachemap")
OffheapDTO對象的定義方式如下:
class OffheapDTO implements Serializable, Comparable{
private String name; private String address; private String mark; private int order; //省略getset }
也就是我可以隨意的把任何對象進行序列化操作,然后可以隨意的把任何已經序列化的對象,反序列化回來。
第一版代碼代碼,未中其意
putMap方法代碼如下:
public <T> void putMap(String key, T value, long expireSeconds) {
try {
EntityWrapper<T> entityWrapper = new EntityWrapper<>().create(key, value, expireSeconds); initMapContainer(OffheapCacheConst.MAP_CONTAINER_FOR_STRING).put(key, JSON.toJSONString(entityWrapper)); } catch (Exception ex) { logger.error(OffheapCacheConst.PACKAGE_CONTAINER + "putMapSingle with expires exception:", ex); throw ex; }
}
queryMap方法代碼如下:
public <T> T queryMap(String key) {
try { Object result = initMapContainer(OffheapCacheConst.MAP_CONTAINER_FOR_STRING).get(key); if(result == null){ return null; } //反序列化出entityWrapper EntityWrapper entityWrapper = JSON.parseObject(result.toString()); return (T)entityWrapper.getEntity(); } catch (Exception ex) { logger.error(OffheapCacheConst.PACKAGE_CONTAINER + "queryMap exception:", ex); return null; }
}
結果當我反序列化的時候,調用resultmap.get("1")的時候,提示我無法將jsonObject轉變成OffheapDTO. 調試進去發現,List對象里面裝載的仍然是jsonObject數據。初次嘗試失敗。
第二版代碼,苦盡甘來
之后翻看了百度,查閱了大量資料,然后看到了關於TypeReference的代碼,之后將queryMap方法修改如下:
public <T> T queryMap(String key) {
try { Object result = initMapContainer(OffheapCacheConst.MAP_CONTAINER_FOR_STRING).get(key); if(result == null){ return null; } //反序列化出entityWrapper EntityWrapper entityWrapper = JSON.parseObject(result.toString(),new TypeReference<EntityWrapper<T>>() {});
return (T)entityWrapper.getEntity();
} catch (Exception ex) { logger.error(OffheapCacheConst.PACKAGE_CONTAINER + "queryMap exception:", ex); return null; } }
注意代碼中黃色部分。
然后當我再次進行反序列化的時候,我發現resultMap.get(“1”)已經可以拿到正常的OffheapDTO對象了。心中一喜,然后運行resultMap.get(“1”).getName(), 居然又報錯,提示無法將jsonObject轉變成OffheapDTO對象,發現原來存儲的字段,居然都是jsonObject類型。這次就有點慌了。
第三版代碼,驀然回首
不過想想fastjson這么成熟,定然有前人的輪子,所以就繼續查閱資料,終於查到了setAutoTypeSupport這個屬性,沒想到一試,居然解決了問題。
首先,程序啟動的時候,需要開啟這個屬性,至於這個屬性真正的意義,去翻閱fastjson文檔,我這里就不贅述了:
//開啟fastjson autotype功能(不開啟,造成EntityWrapper<T>中的T無法正常解析)
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
然后,在序列化的時候,需要附帶上序列化的class名稱(黃色標記部分):
public <T> void putMap(String key, T value, long expireSeconds) { try { EntityWrapper<T> entityWrapper = new EntityWrapper<>().create(key, value, expireSeconds); initMapContainer(OffheapCacheConst.MAP_CONTAINER_FOR_STRING).put(key, JSON.toJSONString(entityWrapper, SerializerFeature.WriteClassName));
} catch (Exception ex) {
logger.error(OffheapCacheConst.PACKAGE_CONTAINER + "putMapSingle with expires exception:", ex); throw ex; } }
最后,在反序列化的時候,利用TypeReference進行類型指定即可:
public <T> T queryMap(String key) {
try { Object result = initMapContainer(OffheapCacheConst.MAP_CONTAINER_FOR_STRING).get(key); if(result == null){ return null; } //反序列化出entityWrapper EntityWrapper entityWrapper = JSON.parseObject(result.toString(),new TypeReference<EntityWrapper<T>>() {});
return (T)entityWrapper.getEntity();
} catch (Exception ex) { logger.error(OffheapCacheConst.PACKAGE_CONTAINER + "queryMap exception:", ex); return null; } }
這樣,無論你的類有多復雜,都可以搞定,比如像下面這樣的:
Map<String,List< OffheapDTO>> resultmap = offheapCacheWrapper.queryMap("maptest");
甚至這樣的:
List<Map<String,List<Set<OffheapDTO>>>> resultmap = offheapCacheWrapper.queryMap("maptest");
https://www.cnblogs.com/scy251147/p/9451879.html
#打開AutoType功能 在1.2.25之后的版本,以及所有的.sec01后綴版本中,autotype功能是受限的,和之前的版本不同,如果在升級的過程中遇到問題,可以通過以下方法配置。
一、添加autotype白名單
添加白名單有三種方式,三選一,如下:
1. 在代碼中配置
ParserConfig.getGlobalInstance().addAccept("com.taobao.pac.client.sdk.dataobject.");
如果有多個包名前綴,分多次addAccept
2. 加上JVM啟動參數
-Dfastjson.parser.autoTypeAccept=com.taobao.pac.client.sdk.dataobject.,com.cainiao.
如果有多個包名前綴,用逗號隔開
3. 通過fastjson.properties文件配置。
在1.2.25/1.2.26版本支持通過類路徑的fastjson.properties文件來配置,配置方式如下:
fastjson.parser.autoTypeAccept=com.taobao.pac.client.sdk.dataobject.,com.cainiao. // 如果有多個包名前綴,用逗號隔開
二、打開autotype功能
如果通過配置白名單解決不了問題,可以選擇繼續打開autotype功能,fastjson在新版本中內置了多重防護,但是還是可能會存在一定風險。兩種方法打開autotype,二選一,如下:
1、JVM啟動參數
-Dfastjson.parser.autoTypeSupport=true
2、代碼中設置
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
如果有使用非全局ParserConfig則用另外調用setAutoTypeSupport(true);
三、配置autoType黑名單
打開AutoType之后,是基於內置黑名單來實現安全的,但黑名單是窮舉不完的,如果發現了新的風險類,可以通過以下配置來增加黑名單:
1. 配置JVM啟動參數
-Dfastjson.parser.deny=xx.xxx
- 這里的xx.xxx是包名前綴,如果有多個包名前綴,用逗號隔開
2. 通過fastjson.properties來配置
在1.2.25之后的版本支持通過類路徑的fastjson.properties文件來配置,配置方式如下:
-Dfastjson.parser.deny=xx.xxx
- 這里的xx.xxx是包名前綴,如果有多個包名前綴,用逗號隔開
3. 代碼中配置
ParserConfig.getGlobalInstance().addDeny("xx.xxx");
- 這里的xx.xxx是包名前綴,如果有多個包名前綴,用逗號隔開
https://github.com/alibaba/fastjson/wiki/enable_autotype
使用Redis 配置替換fastjson 反序列化報錯 com.alibaba.fastjson.JSONException: autoType is not support
新建的GenericFastJson2JsonRedisSerializer里面添加白名

添加:
static {
ParserConfig.getGlobalInstance().addAccept("com.xxx.xxx.bo");
ParserConfig.getGlobalInstance().addAccept("com.xxx.xxx.redis");
}
參考:https://blog.csdn.net/InnovationAD/article/details/84341687
https://www.cnblogs.com/xyj179/p/10095250.html


總結:在使用fastjson類序列化時,尤其是redis這個序列的支持,在類中要加保證一默認的構造方法,否則在進行反序列化時,fastjosn根據typeName進行反序列化,會發生該異常
安全升級公告
最近發現fastjson在1.2.24以及之前版本存在遠程代碼執行高危安全漏洞,為了保證系統安全,請升級到1.2.28/1.2.29/1.2.30/1.2.31或者更新版本。
1.2.29//1.2.30/1.2.31是在1.2.28版本上修復了一些大家升級過程中遇到的問題的版本,非安全問題,如果升級到1.2.25~1.2.28以及各種sec01版本的,也是沒有安全問題的。
1.2.25/1.2.26/1.2.27/1.2.28/1.2.29/1.2.30都是在升級的過程中修復不兼容問題發布的過度版本,如果你是在此之前升級到這些版本,不用因為這次的安全問題再次升級。
更新方法
1. Maven依賴配置更新
通過maven配置更新,使用最新版本,如下:
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.31</version> </dependency> <!-- 1.2.41是最新的版本 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.41</version> </dependency>
注意,版本號1.2.3~1.2.9版本都比1.2.31小,都是需要升級的。
2. 直接下載
- 1.2.31版本下載地址
http://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.31/ - 1.2.41版本下載地址
http://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.41/
常見問題
1. 升級遇到不兼容問題怎么辦?
1.2.28/1.2.29/1.2.30/1.2.31已經修復了絕大多數兼容問題,但是總會有一些特殊的用法導致不兼容,如果你遇到不兼容問題,通過 https://github.com/alibaba/fastjson/wiki/incompatible_change_list 查看不兼容問題,鏈接的后面提供了遇到不兼容問題之后的使用相應的sec01版本解決辦法。
2. 升級之后報錯autotype is not support
安全升級包禁用了部分autotype的功能,也就是"@type"這種指定類型的功能會被限制在一定范圍內使用。如果你使用場景中包括了這個功能,https://github.com/alibaba/fastjson/wiki/enable_autotype 這里有一個介紹如何添加白名單或者打開autotype功能。
3. 通過配置打開autotype之后是否存在安全漏洞
在1.2.28/1.2.29以及所有的.sec01版本中,有多重保護,但打開autotype之后仍會存在風險,不建議打開,而是使用一個較小范圍的白名單。打開autoType建議升級到最新版本1.2.37以上
4. Android環境使用是否需要升級
目前未發現漏洞對Android系統產生影響,在Android環境中使用不用升級。
5. 升級遇到問題希望提供支持怎么辦?
作者願意幫助大家一起解決問題,如果遇到文檔中沒說明到的問題,請通過如下方式聯系作者:
- 釘釘號 wenshaojin2017
- 微信號 wenshaojin
- 微博 http://weibo.com/wengaotie
6. 有沒有漏洞利用詳情可以提供
為了保證更多用戶的安全,目前不適合擴散漏洞利用的細節
7. 是否有在WAF上檢測的辦法
檢測post內容中是否包含如下字符
"@type"
注意,為了減少誤報,包括雙引號
8. 檢測當前使用版本的是否有問題
- 通過maven dependency檢測
如果是maven工程,在代碼根目錄下執行如下命令,如果有返回,就是需要升級
mvn dependency:tree | grep "com.alibaba.fastjson:" | grep -v sec01 | grep -v 1.2.25 | grep -v 1.2.26 | grep -v 1.2.27 | grep -v 1.2.28 | grep -v 1.2.29 | grep -v 1.2.30 | grep -v 1.2.31
- 在lib目錄下執行如下腳本命令,可以判斷版本是否有問題
ls | grep fastjson | grep jar | grep -v sec01 | grep -v 1.2.25 | grep -v 1.2.26 | grep -v 1.2.27 | grep -v 1.2.28 | grep -v 1.2.29 | grep -v 1.2.30 | grep -v 1.2.31
請注意,為了方便閱讀,加上了換行符,請使用時把上面的三行合並成一行。
- 看打開的文件中是否包含fastjson
sudo -u admin lsof -X | grep -v 1.2.25 | grep -v 1.2.26 | grep -v 1.2.27 | grep -v 1.2.28 | grep -v 1.2.29 | grep -v 1.2.30 | grep -v 1.2.31
請注意,為了方便閱讀,加上了換行符,請使用時把上面的兩行合並成一行。另外通過lsof檢測,在tomcat某些場景是檢測不出來的,最好在lib目錄下用ls檢測(第2中方法)。
參考:
作者:xiaolyuh
鏈接:https://www.jianshu.com/p/a92ecc33fd0d
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。

