FastJson autoType is not support
最近项目里用到了阿里巴巴的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
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。