Jackson 的核心模块由三部分组成。

  • jackson-core,核心包,提供基于"流模式"解析的相关 API,它包括 JsonPaser 和 JsonGenerator。 Jackson 内部实现正是通过高性能的流模式 API 的 JsonGenerator 和 JsonParser 来生成和解析 json。
  • jackson-annotations,注解包,提供标准注解功能;
  • jackson-databind ,数据绑定包, 提供基于"对象绑定" 解析的相关 API ( ObjectMapper ) 和"树模型" 解析的相关 API (JsonNode);基于"对象绑定" 解析的 API 和"树模型"解析的 API 依赖基于"流模式"解析的 API。
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.3</version>
</dependency>
</dependencies>

ObjectMapper

Jackson 最常用的 API 就是基于"对象绑定" 的 ObjectMapper:

  • ObjectMapper可以从字符串,流或文件中解析JSON,并创建表示已解析的JSON的Java对象。 将JSON解析为Java对象也称为从JSON反序列化Java对象。
  • ObjectMapper也可以从Java对象创建JSON。 从Java对象生成JSON也称为将Java对象序列化为JSON。
  • Object映射器可以将JSON解析为自定义的类的对象,也可以解析置JSON树模型的对象。

之所以称为ObjectMapper是因为它将JSON映射到Java对象(反序列化),或者将Java对象映射到JSON(序列化)。

import com.fasterxml.jackson.databind.ObjectMapper;

public class JacksonExample {
public static void main(String[] args) {
String json = "{\"name\":\"John\", \"age\":30}";

ObjectMapper objectMapper = new ObjectMapper();
try {
// 将JSON字符串转换为Java对象
Person person = objectMapper.readValue(json, Person.class);

// 输出转换后的Java对象
System.out.println("Name: " + person.getName());
System.out.println("Age: " + person.getAge());
} catch (Exception e) {
e.printStackTrace();
}
}
}

class Person {
private String name;
private int age;

// 必须提供无参构造函数
public Person() {
}

// Getters and Setters

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}
}

将对象转化为json

  • writeValue()
  • writeValueAsString()
  • writeValueAsBytes()
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.FileOutputStream;

public class JacksonExample {
public static void main(String[] args) {
ObjectMapper objectMapper = new ObjectMapper();
Person person = new Person();
person.setAge(123);
person.setName("fakes0u1");

try {
String jsonstring = objectMapper.writeValueAsString(person);
System.out.println(jsonstring);
} catch (Exception e) {
e.printStackTrace();
}
}
}

class Person {
private String name;
private int age;

// 必须提供无参构造函数
public Person() {
}

// Getters and Setters

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}
}

JsonParser

Jackson JsonParser类是一个底层一些的JSON解析器。 它类似于XML的Java StAX解析器,差别是JsonParser解析JSON而不解析XML。

Jackson JsonParser的运行层级低于Jackson ObjectMapper。 这使得JsonParser比ObjectMapper更快,但使用起来也比较麻烦。

使用JsonParser需要先创建一个JsonFactory

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;

public class JacksonJsonParser {
public static void main(String[] args){
String json = "{\"name\":\"fakes0u1\",\"age\":123}";
JsonFactory jsonFactory = new JsonFactory();
try {
JsonParser parser = jsonFactory.createParser(json);
System.out.println(parser);
}
catch (Exception e ){
e.printStackTrace();
}
}
}
//person类就不写了,标准的javabean

一旦创建了Jackson JsonParser,就可以使用它来解析JSON。 JsonParser的工作方式是将JSON分解为一系列令牌,可以一个一个地迭代令牌。

这是一个JsonParser示例,它简单地循环遍历所有标记并将它们输出到System.out。 这是一个实际上很少用示例,只是展示了将JSON分解成的令牌,以及如何遍历令牌的基础知识。

可以使用JsonParser的nextToken()获得一个JsonToken。 可以使用此JsonToken实例检查给定的令牌。 令牌类型由JsonToken类中的一组常量表示。 这些常量是

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;

public class JacksonJsonParser {
public static void main(String[] args){
String json = "{\"name\":\"fakes0u1\",\"age\":123}";
JsonFactory jsonFactory = new JsonFactory();
try{
JsonParser parser = jsonFactory.createParser(json);
while(!parser.isClosed()){
JsonToken jsonToken = parser.nextToken();
System.out.println(jsonToken);
}
}
catch (Exception e ){
e.printStackTrace();
}
}
}


START\_OBJECT
FIELD\_NAME
VALUE\_STRING
FIELD\_NAME
VALUE\_NUMBER\_INT
END\_OBJECT
null

使用equals方法 检查如果标记的字段名称是相同的 就返回其值

指向的令牌是字符串字段值,则getValueAsString()返回当前令牌值作为字符串。 如果指向的令牌是整数字段值,则getValueAsInt()返回当前令牌值作为int值。 JsonParser具有更多类似的方法来获取不同类型的curren令牌值(例如boolean,short,long,float,double等)

JsonGenerator

Jackson JsonGenerator用于从Java对象(或代码从中生成JSON的任何数据结构)生成JSON。

同样的 使用JsonGenerator也需要先创建一个JsonFactory 从其中使用createGenerator() 来创建一个JsonGenerator

import com.fasterxml.jackson.core.*;

import java.io.File;

public class JacksonJsonParser {
public static void main(String[] args){
JsonFactory jsonFactory = new JsonFactory();
Person1 person1 =new Person1();
try{
JsonGenerator jsonGenerator = jsonFactory.createGenerator(new File("output.json"), JsonEncoding.UTF8);
jsonGenerator.writeStartObject();
jsonGenerator.writeStringField("name","fakes0u1");
jsonGenerator.writeNumberField("age",123);
jsonGenerator.writeEndObject();

jsonGenerator.close();

}
catch (Exception e ){
e.printStackTrace();
}
}
}

多态问题

Java多态就是同一个接口使用不同的实例而执行不同的操作

在Jackson中 JacksonPolymorphicDeserialization可以解决这个问题 在反序列化某个类对象的过程中 如果类的成员不是具体类型 比如是Object 接口 或者 抽象类 那么可以在JSON字符串中 指定其类型 Jackson将生成具体类型的实例

具体来说就是 将具体的子类信息绑定在序列化的内容中 以便于后续反序列化的时候 直接得到目标子类对象 我们可以通过DefaultTyping 和 @JsonTypeInfo 注解来实现

DefaultTyping

四个值:

1 .JAVA_LANG_OBJECT:当被序列化或反序列化的类里的属性被声明为一个Object类型时,会对该Object类型的属性进行序列化和反序列化,并且明确规定类名。(当然,这个Object本身也得是一个可被序列化的类)
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.JAVA_LANG_OBJECT);
输出

{"name":"fakes0u1","age":123,"object":["jackson.Hacker",{"skill":"moyu"}]}
Person2.age=123,Person2.name=fakes0u1,jackson.Hacker@f6c48ac

没设置的时候会输出

{"name":"fakes0u1","age":123,"object":{"skill":"moyu"}}
Person2.age=123,Person2.name=fakes0u1,{skill=moyu}
  1. OBJECT_AND_NON_CONCRETE: 当类中有Interface AbstractClass类时 对其进行序列化和反序列化 这也是enableDefaultTyping() 的默认选项
{"name":"fakes0u1","age":123,"object":["jackson.Hacker",{"skill":"moyu"}],"sex":["jackson.MySex",{"sex":0}]}
Person2.age=123,Person2.name=fakes0u1,jackson.Hacker@239963d8,jackson.MySex@3abbfa04

看到接口也被成功的序列化和反序列化

  1. NON_CONCRETE_AND_ARRAYS:支持Arrays类型,可以在原来的test文件上直接修改
Hacker[] hacker = new Hacker[2];
hacker[0] = new Hacker();
hacker[1] = new Hacker();
person2.object = hacker;
person2.sex = new MySex();



{"name":"fakes0u1","age":123,"object":["[Ljackson.Hacker;",[{"skill":"moyu"},{"skill":"moyu"}]],"sex":["jackson.MySex",{"sex":0}]}
Person2.age=123,Person2.name=fakes0u1,[Ljackson.Hacker;@e45f292,jackson.MySex@5f2108b5
  1. NON_FINAL:除了前面所有的特征外 包含即将被序列化的类里的全部、非final的属性将其进行序列化和反序列化
DefaultTyping类型 描述说明
JAVA_LANG_OBJECT 属性的类型为Object
OBJECT_AND_NON_CONCRETE 属性的类型为Object、Interface、AbstractClass
NON_CONCRETE_AND_ARRAYS 属性的类型为Object、Interface、AbstractClass、Array
NON_FINAL 所有除了声明为final之外的属性

@JsonTypeInfo注解

加上注释。

  1. JsonTypeInfo.Id.NONE:用于指定在序列化和反序列化过程中不包含任何类型标识 不使用识别码,输出没有什么不一样
  2. JsonTypeInfo.Id.CLASS:反序列化的时候通过@class指定相关类,使用完全限定类名做识别
{"name":"fakes0u1","age":123,"object":{"@class":"jackson.Hacker","skill":"moyu"}}
Person2.age=123,Person2.name=fakes0u1,jackson.Hacker@5702b3b1
  1. JsonTypeInfo.Id.MINIMAL_CLASS:和JsonTypeInfo.Id.CLASS差不多,只不过@class变成了@c
{"name":"fakes0u1","age":123,"object":{"@c":"jackson.Hacker","skill":"moyu"}}
Person2.age=123,Person2.name=fakes0u1,jackson.Hacker@4b952a2d
  1. JsonTypeInfo.Id.NAME:多了@type,但是没法被反序列化利用。序列化的输出变为
{"name":"fakes0u1","age":123,"object":{"@type":"Hacker","skill":"moyu"}}

多出一个@type 这里并没有像上面的CLASS一样 给出具体包名和类名 同时在反序列化的时候还会报错 也就是说 这个注释并不适用于反序列化过程
5. JsonTypeInfo.Id.CUSTOM:需要自定义,手写解析器。

所以JsonTypeInfo.Id.CLASSJsonTypeInfo.Id.MINIMAL_CLASS可以触发反序列化漏洞。

Jackson反序列化漏洞

满足下面三个条件之一即存在Jackson反序列化漏洞:

  • 调用了ObjectMapper.enableDefaultTyping()函数;
  • 对要进行反序列化的类的属性使用了值为JsonTypeInfo.Id.CLASS的@JsonTypeInfo注解;
  • 对要进行反序列化的类的属性使用了值为JsonTypeInfo.Id.MINIMAL_CLASS的@JsonTypeInfo注解;

如果反序列化的类的属性是Object的时候,因为Object类型是任意类型的父类,因此扩大了我们的攻击面,我们只需要寻找出在目标服务端环境中存在的且构造函数或setter方法存在漏洞代码的类即可进行攻击利用

属性中没有Object

只能让他的构造函数或者是setter方法中存在危险函数

属性中有Object类

我们只需要在目标服务端中存在的且构造函数或setter方法存在漏洞的类即可进行攻击利用 例如 存在一个恶意类Evil 在其构造函数或者是setter方法中存在任意代码执行漏洞