文章目录
- 序列化与反序列化
- 一.什么是序列化与反序列化?
- 二. 序列化与反序列化的原理
- 三.应用场景
- 四.原生序列化与反序列化的实现
- 序列化
- 反序列化
- 五.漏洞成因
序列化与反序列化
一.什么是序列化与反序列化?
Java 序列化是指把 Java 对象转换为字节序列的过程;
Java 反序列化是指把字节序列恢复为 Java 对象的过程;
二. 序列化与反序列化的原理
Java序列化机制是基于对象的类结构进行的。当一个对象需要被序列化时,Java会将其转换为字节流,包括对象的数据和类的信息。这个字节流可以存储到文件中、传输到网络上,或者在分布式系统中传递给其他节点。
反序列化是将字节流转换回对象的过程。在反序列化过程中,Java会使用字节流中的信息重构对象,并将其重新加载到内存中。
Java的序列化机制是通过实现java.io.Serializable接口来实现的。该接口是一个标记接口,没有任何方法定义。只有实现了Serializable接口的类的对象才能被序列化。
三.应用场景
其一,实现了数据的持久化,通过序列化可以把数据永久的保存在硬盘上;其二,利用序列化实现远程通信,即在网络上传递对象的字节序列。
四.原生序列化与反序列化的实现
使用到JDK中关键类 **ObjectOutputStream**(对象输出流) 和**ObjectInputStream**(对象输入流)
ObjectOutputStream 类中:通过使用 writeObject(Object object) 方法,将对象以二进制格式进行写入。
ObjectInputStream 类中:通过使用 readObject()方法,从输入流中读取二进制流,转换成对象。
序列化
要将对象序列化,可以按照以下步骤进行操作:
- 实现Serializable接口:将要序列化的类实现Serializable接口。
- 创建ObjectOutputStream:创建一个ObjectOutputStream对象,用于将对象序列化为字节流。
- 写入对象:使用writeObject()方法将对象写入到输出流中。
- 关闭流:关闭输出流。
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
class Person implements Serializable {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
public class SerializationExample {
public static void main(String[] args) {
Person person = new Person("John Doe", 30);
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.ser"))) {
oos.writeObject(person);
} catch (IOException e) {
e.printStackTrace();
}
}
}
查看 person.ser 一堆乱码
反序列化
- 要将字节流反序列化为对象,可以按照以下步骤进行操作:
- 创建ObjectInputStream:创建一个ObjectInputStream对象,用于从字节流中读取对象。
- 读取对象:使用readObject()方法从输入流中读取对象。
- 关闭流:关闭输入流。
import java.io.FileInputStream;
import java.io.IOException
;
import java.io.ObjectInputStream;
public class DeserializationExample {
public static void main(String[] args) {
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.ser"))) {
Person person = (Person) ois.readObject();
System.out.println("Name: " + person.getName());
System.out.println("Age: " + person.getAge());
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
五.漏洞成因
官方允许用户在被序列化的类中重写 readObject() 方法 , 重写后的方法将负责在反序列化时重构当前类对象 . 用户只需要在重写的 readObject() 方法中实现 defaultReadObject() 方法 , 就可以确保反序列化过程正常执行 .
序列化前,在 person 类中加入重写的 readObject 方法
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.defaultReadObject();
Runtime.getRuntime().exec("calc");
}
那么反序列化调用的就是用户重写的 readObject。