☰
fastjson 1.2.83 升到 1.2.84 要改代码吗?实测:动态数据源会报 autoType is not support,前缀白名单反序列化慢 13 倍
2026/10/16 3:16:53 网站建设 项目流程

最近后台总有人搜「fastjson 1.2.84 解析报错」「fastjson 1.2.84 和 1.2.83 区别」。1.2.84 是只修安全问题的小版本,照理说直接换版本号就行。我把 1.2.83 和 1.2.84 放在一起,对同一批写法逐条跑了一遍,结果有两处要注意。

先说结论

  • 大部分写法不受影响:普通对象、TypeReference泛型、不带白名单的@type(两版都拒)、SafeMode、全开 autoType 往返、字段声明父类 +@type子类,两版结果完全一样。
  • 会报错的一种:用addAccept("com.xxx.")这种包前缀白名单,反序列化一个实现了javax.sql.DataSource的类(比如继承 SpringAbstractRoutingDataSource的动态数据源)。1.2.83 正常,1.2.84 报autoType is not support。
  • 会变慢的两种:同样是包前缀白名单,以及不开 autoType 时加了@JSONType的类。带@type的反序列化,JDK 8 上慢约 13 倍和 23 倍,JDK 17 上两种都慢约 28 倍。
  • 一个办法解决两个问题:把白名单从包前缀改成完整类名。实测报错消失,耗时也回到和 1.2.83 一样。
  • 官方发布说明里没有提这两处变化。

官方改了什么

1.2.83 到 1.2.84 一共 4 个提交,主代码只动了两个文件:

modified +56/-6 src/main/java/com/alibaba/fastjson/parser/ParserConfig.java modified +43/-0 src/main/java/com/alibaba/fastjson/util/TypeUtils.java modified +20/-1 pom.xml added +147/-0 src/test/java/.../autoType/AutoTypeValidationTest.java

发布说明列了 4 条修复:拒绝类型名里带:/!;白名单 hash 命中后再比一次原文;包前缀白名单不再放行ClassLoader/DataSource/RowSet这类危险基类;危险类不再在黑名单检查之前被放进缓存。

这次要修的漏洞是 CVE-2026-16723,版本该怎么看,前一篇已经按版本轴梳理过:fastjson 1.2.49 中了 CVE-2026-16723 吗?1.2.84 和 1.2.83 到底差在哪。还在 JDK 7 上的项目,1.2.84 根本加载不起来,另一篇有实测:fastjson 1.2.84 能在 JDK 7 上跑吗?。本文只讲 JDK 8 及以上升级后行为有没有变。

实测方法

  • jar 直接从 Maven Central 下载,SHA1 与 Central 上的.sha1一致:1.2.839ee94951bc107d382519975d04bc950b6c6ab297,1.2.8405773609a8ce27cc9cbfbec0009e02b1a7b83dde。
  • 同一份测试代码分别对两个 jar 编译、运行。fastjson 的类缓存是全局静态的,混在一个进程里跑会互相污染,所以每个用例单独起一个 JVM。
  • JDK:Zulu1.8.0_504、Oracle17.0.4.1。

功能对照:17 种写法,4 种结果不同(归结起来是 3 处变化)

写法1.2.831.2.84
JSON.VERSION1.2.831.2.83(没改,见第 34 注那篇)
普通对象parseObject(json, User.class)+ 序列化✅✅
TypeReference<List<User>>泛型✅✅
带@type、默认配置(没开 autoType、没白名单)❌autoType is not support❌ 同左
带@type+addAccept("demo.")包前缀✅✅
白名单写内部类的二进制名addAccept("demo.Outer$Inner")❌autoType is not support✅1.2.84 修好了
白名单写内部类的点号名addAccept("demo.Outer.Inner")✅✅
包前缀白名单 + 实现了 DataSource 的类(不开 autoType)✅❌autoType is not support. demo.MyDataSource
包前缀白名单 + 实现了 DataSource 的类(开 autoType)✅❌同上
完整类名白名单addAccept("demo.MyDataSource")✅✅
只开 autoType、不配白名单,反序列化 DataSource 类❌❌ 同左
DataSource 类作为普通字段类型(不靠@type)✅✅
全开 autoType,WriteClassName序列化再读回✅✅
@JSONType注解类 +@type(不开 autoType)✅✅
SafeMode + 白名单❌safeMode not support autoType❌ 同左
类型名带冒号demo:User(开 autoType)❌ClassCastException(报错很怪)❌autoType is not support. demo:User
字段声明父类Animal,JSON 里@type写子类Dog✅✅

另外用真实的 Spring 动态数据源测了一次(spring-jdbc 5.3.39,一个继承AbstractRoutingDataSource的类,不开 autoType):

白名单写法1.2.831.2.84
addAccept("demo.")✅❌autoType is not support. demo.DynDs
addAccept("demo.DynDs")✅✅

那条报错为什么不好查

1.2.84 拒绝这类类时,报的是autoType is not support. 类名,和「没配白名单」时的报错一字不差。白名单明明写了com.xxx.,报错却像白名单没生效,很容易往「配置没加载上」的方向查。

原因在ParserConfig.checkAutoType新加的这几行(1.2.84 源码第 1436~1444 行):

if(clazz!=null){// matching an accept prefix is not an opt-in for gadget base types, only an// accept entry naming the type in full is; keep scanning for such an entryif(i+1<className.length()&&TypeUtils.isAutoTypeDenyClass(clazz)){continue;}returnclazz;}

isAutoTypeDenyClass判断的是ClassLoader/javax.sql.DataSource/javax.sql.RowSet的子类。也就是说,只有白名单写完整类名,才算你明确放行了这个类,包前缀不算。这是安全上有意的收紧,发布说明第 3 条说的就是它。

性能对照:两种写法慢了一个数量级

同一条 JSON 反序列化 100 万次,先热身 20 万次,取每次平均耗时。每组单独起 JVM,跑 3 轮取中位数,单位纳秒(ns)。这是单机微基准,看倍数,别看绝对值。

写法JDK 8:1.2.83JDK 8:1.2.84JDK 17:1.2.83JDK 17:1.2.84
普通对象(不带@type)46447378
完整类名白名单 +@type121127155163
全开 autoType +@type159185206206
包前缀白名单 +@type1241,658(约 13 倍)1534,326(约 28 倍)
@JSONType类 +@type(不开 autoType)1052,396(约 23 倍)1594,518(约 28 倍)
开 autoType同时配包前缀白名单 +@type138421(约 3 倍)150285(约 2 倍)

三轮原始数据(1.2.84,包前缀白名单):JDK 8 是 1,642 / 1,658 / 1,687,JDK 17 是 4,326 / 3,740 / 4,535,各轮之间很稳定。

按 JDK 17 的数字算,一个响应里有 1,000 个带@type的对象,每次反序列化就多出约 4 毫秒。

慢在哪

用 JFR 在 JDK 17 上采样 1.2.84 的这两种写法,93%~94% 的采样都落在同一个方法上:com.alibaba.fastjson.util.IdentityHashMap.findClass。

这个方法会把ParserConfig里反序列化器表的每一个桶都扫一遍,桶数默认是 8192(IdentityHashMap.DEFAULT_SIZE = 8192):

publicClassfindClass(StringkeyString){for(inti=0;i<buckets.length;i++){...if(className.equals(keyString)){returnclazz;}}returnnull;}

1.2.83 走不到这里,原因是类在第一次之后就进了全局缓存:

// 1.2.83:前缀白名单命中后加载类,第三个参数 true = 放进缓存clazz=TypeUtils.loadClass(typeName,defaultClassLoader,true);

1.2.84 改成了只有完整类名命中才缓存:

// 1.2.84:i + 1 == className.length() 只在完整类名命中时为 trueclazz=TypeUtils.loadClass(typeName,defaultClassLoader,i+1==className.length());

@JSONType那条路也一样:1.2.83 在autoTypeSupport || jsonType时缓存;1.2.84 改成loadClass(typeName, defaultClassLoader, false),之后只在开了 autoType 时才调用addMapping。

没进缓存,下一次checkAutoType先查缓存(TypeUtils.getClassFromMapping)落空,接着就调deserializers.findClass(typeName),每次都要把 8192 个桶扫一遍。

源码注释写明了不缓存是故意的(「a prefix match … must not populate the class mapping cache」),为的是不让危险类从缓存里绕过黑名单。所以这是安全收紧带来的性能代价,不是写错了。发布说明里没有提这一点。

怎么确认就是缓存的问题

做了两组对照:

写法JDK 8:1.2.84JDK 17:1.2.84
包前缀白名单,另外手动TypeUtils.addMapping("demo.User", User.class)114(恢复)164(恢复)
@JSONType类,另外addAccept("demo.Tagged")完整类名92(恢复)152(恢复)

把类放回缓存,耗时就和 1.2.83 一样了。第一组只是为了验证原因,不建议在业务里这么写:按源码,checkAutoType会先查这个缓存,命中就直接返回,白名单检查就被跳过了。

升级前怎么自查

在代码里搜这几样:

grep-rn--include=*.java-e"addAccept"-e"@JSONType"-e"AutoTypeSupport"src/grep-rn--include=*.java-e"implements DataSource"-e"AbstractRoutingDataSource"src/grep-rn"fastjson.parser.autoTypeAccept".# 配置文件或启动参数 -D 里写的白名单,走的也是 addAccept

对照下面这张表:

你的情况升 1.2.84 后怎么办
不用@type,只是普通的parseObject/toJSONString没变化直接升
开了 SafeMode没变化直接升
全开 autoType、没配白名单(比如 fastjson 自带的GenericFastJsonRedisSerializer,它内部就是setAutoTypeSupport(true))功能没变化;JDK 8 上耗时中位数 159 → 185 ns,JDK 17 上持平直接升。全开 autoType 本身就是这次漏洞要防的场景,能关最好关
addAccept("com.xxx.")包前缀白名单带@type的反序列化慢一个数量级;其中 DataSource 子类直接报错改成完整类名,一个类一行
@JSONType注解类,不开 autoType,靠@type反序列化慢一个数量级把这些类也用完整类名addAccept一遍

改法示例:

ParserConfigconfig=ParserConfig.getGlobalInstance();// 1.2.84 下会慢,DataSource 子类会被拒// config.addAccept("com.example.");// 改成完整类名config.addAccept("com.example.order.OrderDTO");config.addAccept("com.example.user.UserDTO");config.addAccept("com.example.ds.DynamicDataSource");

内部类写Outer$Inner或Outer.Inner都行(1.2.84 把两种写法统一了;1.2.83 只认点号写法)。

复现代码

完整代码就两个类,核心部分如下:

// 功能对照:每个用例单独起 JVM 跑ParserConfigcfg=newParserConfig();cfg.addAccept("demo.");Objecto=JSON.parseObject("{\"@type\":\"demo.MyDataSource\",\"url\":\"x\"}",Object.class,cfg);// 1.2.83: demo.MyDataSource// 1.2.84: JSONException: autoType is not support. demo.MyDataSource
// 性能对照:热身 20 万次,再计时 100 万次ParserConfigcfg=newParserConfig();cfg.addAccept("demo.");Stringjson="{\"@type\":\"demo.User\",\"id\":1,\"name\":\"tom\"}";for(inti=0;i<200_000;i++)JSON.parseObject(json,Object.class,cfg);longt0=System.nanoTime();for(inti=0;i<1_000_000;i++)JSON.parseObject(json,Object.class,cfg);System.out.println((System.nanoTime()-t0)/1_000_000+" ns/次");
javac-cpfastjson-1.2.84.jar-dout src/demo/*.javajava-cp"fastjson-1.2.84.jar;out"demo.Bench accept_prefix# Linux 下分号换冒号

demo.MyDataSource就是一个实现了javax.sql.DataSource、多一个url属性的普通类,方法全是空实现。

数据来源(均为 2026-10-14 读取 / 实跑)

  • 1.2.83 → 1.2.84 的改动:https://github.com/alibaba/fastjson/compare/1.2.83...1.2.84
  • 发布说明:https://github.com/alibaba/fastjson/releases/tag/1.2.84
  • 源码行号:tag1.2.84的ParserConfig.java与IdentityHashMap.java
  • jar:https://repo1.maven.org/maven2/com/alibaba/fastjson/

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询