Java序列化校验需重写private void readObject(ObjectInputStream),在in.defaultReadObject()后检查字段合法性并抛InvalidObjectException;配合transient、readResolve及禁用原生反序列化提升安全性。

Java 序列化机制本身不自动校验数据合法性,但允许你在反序列化过程中插入自定义逻辑——核心方式是重写 private void readObject(ObjectInputStream in) 方法,在字段还原后立即执行检查。
重写 readObject 是校验入口
只要类实现了 Serializable,JVM 在反序列化时会查找这个严格签名的私有方法。一旦存在,就会优先调用它,而不是走默认流程。
- 方法必须声明为
private void readObject(ObjectInputStream),参数类型不能是父类或子类 - 必须在方法内显式调用
in.defaultReadObject(),否则所有非 transient 字段都不会被赋值 - 校验代码必须写在
defaultReadObject()之后,此时this已指向待恢复对象,字段值已就位
在 readObject 中做字段级合法性检查
此时可对任意非 transient 字段做业务规则判断,比如范围、格式、非空、枚举值等。失败时抛出 InvalidObjectException,这是专门为此场景设计的异常。
Java开发手册规约集合,基于阿里巴巴Java开发手册(嵩山版)。 涵盖7大维度:编程规约、异常日志、单元测试、安全规约、MySQL数据库、工程结构、设计规约。
- 年龄字段超限:
if (age 150) throw new InvalidObjectException("age out of range"); - 邮箱格式校验:
if (!email.matches("^[\w.-]+@[\w.-]+\.[a-z]{2,}$")) throw new InvalidObjectException("invalid email"); - 引用对象非空:
if (profile == null) throw new InvalidObjectException("profile must not be null");
配合 transient 和 readResolve 防绕过
仅靠 readObject 不足以覆盖全部风险,需组合使用其他机制:
立即学习“Java免费学习笔记(深入)”;
- 把敏感字段(如 token、password)声明为
transient,它们不会被自动序列化;若仍需传输,应在readObject中手动读取并校验(如in.readUTF()),再做清洗或加密验证 - 单例类务必补充
private Object readResolve(),返回唯一实例,防止反序列化伪造新对象破坏单例契约 - 避免在
readObject中调用可能被子类重写的普通方法(如 getter),以防校验逻辑被绕过
注意原生序列化的根本局限
这套机制依赖 Java 原生序列化,而该机制本身存在严重安全隐患(如 gadget chain 远程代码执行)。生产环境不应将其作为主要防护手段:
- 优先禁用原生反序列化:通过 JVM 参数
-Djdk.serialFilter=...设置白名单过滤器,或升级到 Java 17+ 后强制启用 - 敏感数据传输前应签名+加密,而非依赖序列化层保护
- 对外接口推荐使用 JSON(如 Jackson)替代原生序列化,配合 Spring Validation 做参数级校验,更可控也更主流

















