Java安全
Java 基础知识
01概述
02变量
03运算符
04程序控制语句
05数组
06面向对象编程
07代码练习
08重载
09作用域
10构造方法&构造器
11this
12包
13修饰符
14封装
15继承
16super
17覆盖&重写
18多态
19零钱通项目
20类变量&类方法
21抽象类
22接口
23内部类
24枚举
25泛型
26常用API
27lambda表达式
28正则表达式
29异常
30File&IO流
31日志技术
32多线程
33网络编程
01反射
02反序列化
03JVM
04JDBC
05RMI
06JRMP
07JNDI
08CDI
09JPA
10Servlet
11Filter
12MVC模型
13MVC框架
14类的加载机制
15Maven
16注解
17ORM
18CC链
19JNDI注入
Log4j2
-
+
首页
02反序列化
## 概述 **序列化(Serialization)**:将 Java 对象转换为字节流(byte stream)的过程,目的是便于存储(如写入文件)或网络传输。 **反序列化(Deserialization)**:将字节流恢复为 Java 对象的过程,是序列化的逆操作。 这两个过程依赖 Java 的java.io.Serializable接口(标记接口,无具体方法)和 ObjectOutputStream/ObjectInputStream工具类。 ## 作用 序列化与反序列化解决了 “对象跨介质传输 / 存储” 的问题: - **持久化**:将对象保存到文件或数据库(如游戏存档、用户会话 Session 的存储); - **跨进程通信**:在分布式系统中,通过网络传输对象(如 RPC 框架、微服务间的对象传递)。 ## 实现方式 ### 实现Serializable接口 一个类的对象要能被序列化,必须让类实现 java.io.Serializable(一个标记接口,无任何方法,仅用于告知 JVM 该类允许被序列化)。 ```java import java.io.Serializable; public class Name implements Serializable { private static final long serialVersionUID = 1L; private String name; private int age; public Name(String name, int age) { this.name = name; this.age = age; } } ``` ### 序列化操作 通过ObjectOutputStream的writeObject(Object obj)方法,将对象转换为字节流并写入输出流(如文件输出流、网络输出流)。 ```java public class App { public static void main(String[] args) throws IOException { User user = new User("毛林", 18); System.out.println("原始对象:" + user); //2. 序列化对象:写入到user.obj文件中 try(ObjectOutputStream oos = new ObjectOutputStream(Files.newOutputStream(Paths.get("user.obj")))){ oos.writeObject(user); System.out.println("对象序列化并且已经写入文件"); } catch (Exception e) { e.printStackTrace(); } } } ``` ### 反序列化 通过ObjectInputStream的readObject()方法,从输入流(如文件输入流、网络输入流)中读取字节流,恢复为 Java 对象。 ```java import java.io.*; import java.nio.file.Files; import java.nio.file.Paths; public class App { public static void main(String[] args) throws IOException { User user = new User("毛林", 18); System.out.println("原始对象:" + user); //3. 反序列化:从文件中读取对象 try(ObjectInputStream ois = new ObjectInputStream(Files.newInputStream(Paths.get("user.obj")))){ User deserializedUser = (User) ois.readObject(); System.out.println("反序列化后的对象:" + deserializedUser); } catch (Exception e) { e.printStackTrace(); } } } ``` ## 细节 ### serialVersionUID 序列化时,JVM 会为类生成一个默认的serialVersionUID(长整型),用于标识类的版本。反序列化时,JVM 会检查字节流中的serialVersionUID与当前类的是否一致: - 一致:反序列化成功; - 不一致:抛出InvalidClassException(如类结构修改后,默认serialVersionUID会变化)。 显式声明serialVersionUID,避免类结构微调(如新增字段)导致反序列化失败: ```java public class User implements Serializable { // 显式声明版本号(任意长整数) private static final long serialVersionUID = 1L; private String name; private int age; } ``` ### 不可序列化的字段 被transient修饰的字段**不会被序列化**,反序列化时会恢复为默认值(如null、0)。通常用于敏感信息(如密码)或无需持久化的临时数据: ```java public class User implements Serializable { private static final long serialVersionUID = 1L; private String name; transient private String password; // 密码不序列化 public User(String name, String password) { this.name = name; this.password = password; } } // 序列化后反序列化时,password会变为null ``` ### 静态字段不参与序列化 静态字段属于类(而非对象),序列化仅针对对象的实例数据,因此静态字段不会被序列化: ```java public class User implements Serializable { public static String type = "user"; // 静态字段,不序列化 private String name; } // 即使序列化时type为"user",反序列化后若类中type被修改,会显示修改后的值 ``` ### 父类的序列化规则 若父类未实现Serializable,则父类的字段不会被序列化,反序列化时会调用父类的无参构造器初始化父类字段; 若父类实现Serializable,则父类字段会随子类一起序列化。 ## 安全风险 反序列化本身是中性机制,但**当输入的字节流不可信(如来自网络、未验证的文件)时,可能被攻击者利用执行恶意代码** 。 > 示例:readObject()方法的后门 反序列化时,ObjectInputStream.readObject() 会根据字节流中的类信息,调用对应类的 readObject() 方法(若自定义了的话)。 如果攻击者构造恶意字节流,且目标类的 readObject() 中包含危险操作(如执行命令、修改文件等),反序列化过程就会触发这些操作。 ```java import java.io.*; class EvilClass implements Serializable { // 建议显式定义serialVersionUID(版本号),避免类结构变化导致反序列化失败 private static final long serialVersionUID = 1L; // 自定义readObject,包含恶意操作 private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { ois.defaultReadObject(); // 先执行默认反序列化逻辑 // 模拟恶意操作:执行系统命令(Windows示例) try { Runtime.getRuntime().exec("calc.exe"); // 弹出计算器 } catch (Exception e) { e.printStackTrace(); } } } public class DeserializationAttack { public static void main(String[] args) throws IOException, ClassNotFoundException { // 攻击者生成恶意对象的序列化字节流(实际场景中可能通过网络传输) EvilClass evil = new EvilClass(); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("evil.ser")); oos.writeObject(evil); oos.close(); // 受害者反序列化不可信的字节流,触发恶意代码 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("evil.ser")); ois.readObject(); // 执行到这里时,会弹出计算器 ois.close(); } } ```
毛林
2025年10月27日 20:56
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
PDF文档(打印)
分享
链接
类型
密码
更新密码