1089 字
约 3 分钟
8
S06-J02 · 序列化陷阱:版本兼容、循环引用与反序列化安全

适用读者:要把对象存进 Redis/文件、或做接口兼容的人。
前置:S06-B03。
实测环境:JDK 21(serialVersionUID 实测,2026-11-05)。
覆盖旧文:—(E 驱动)
覆盖扩展:E-S06-009(版本兼容)、E-S06-010(反序列化安全)。
上一篇:S06-J01 · 多模块工程结构:parent / BOM / 聚合
下一篇:S06-J03 · 代码生成。


1. 一句话结论

序列化的所有麻烦都来自同一件事:类的定义会变、数据却已经存下来了。 所以要么"让数据自描述"(JSON),要么"严格管版本"(原生序列化)。


2. 实测:serialVersionUID 不一致会怎样(E-S06-009)

写入时 serialVersionUID = 1L:

1. 写入(UID=1):62 字节

用 serialVersionUID = 2L 读同一个文件:

结果: java.io.InvalidClassException / User; local class incompatible:
      stream classdesc serialVersionUID = 1, local class serialVersionUID = 2
情况 结果
UID 相同、字段新增 能读(新字段为默认值)
UID 不同 InvalidClassException
UID 不同但字段完全一致 仍然失败(UID 就是唯一判据)

推论:显式声明 serialVersionUID 是"承诺"——承诺后,字段变化由你负责兼容;不声明则每次编译可能生成不同 UID,跨版本必然失败。


3. 版本兼容的三种策略

策略 做法 适用
向后兼容(新读旧) 新字段给默认值 常见
向前兼容(旧读新) 旧版本忽略未知字段 JSON 天然支持
强制升级 UID 变更,旧数据作废 数据结构大改

JSON 的优势:字段名自描述,旧代码读新数据只忽略多余字段(配置 FAIL_ON_UNKNOWN_PROPERTIES=false)。这也是"对外接口用 JSON"的原因之一。


4. 反序列化安全(E-S06-010)

Java 原生反序列化 = 把字节流转成对象,且会执行 readObject 等方法——这意味着攻击者可以构造字节流触发任意代码。

风险 说明
Gadget 链 利用库中已有的 readObject 组合执行命令
类加载 反序列化会加载并初始化类
历史事件 多个知名框架因此爆过 RCE

防护四条:

措施 说明
不用原生序列化接收不可信数据 首选(最有效)
ObjectInputFilter(JDK 9+) 白名单类
只接收可信来源(内网/签名) 边界控制
用 JSON + 严格类型 减少攻击面(但注意多态类型 enableDefaultTyping 也是风险)

判据:"这份字节流会从外部来吗?" 会 → 不要用原生反序列化。


5. 循环引用与懒加载(补 B03)

问题 现象 解法
循环引用 JsonMappingException @JsonIgnore / DTO
JPA 懒加载 序列化时触发额外查询或 LazyInitializationException DTO + 显式字段
大对象 序列化后体积暴涨 只暴露必要字段

6. 五个误区

误区 真相
"UID 相同就能随便改字段" 新增可(默认值),删除/改类型要评估
"不写 UID 更省事" 每次编译可能不同 → 跨版本失败
"反序列化只是解析数据" 可能执行代码(RCE)
"JSON 就绝对安全" 多态类型配置不当同样危险
"序列化实体直接返回最方便" 泄露结构、触发懒加载

7. 自检

  1. UID 不同但字段一致,能反序列化吗?
  2. 不写 UID 的风险是什么?
  3. 为什么 JSON 天然比原生序列化"向前兼容"?
  4. 原生反序列化为什么可能 RCE?四条防护里哪条最有效?
  5. 循环引用有哪三种解法?

8. 下一篇

S06-J03 · 代码生成:注解处理器与脚手架

序列化讲完,最后一篇进阶层:生成代码该不该进版本库、Lombok 与脚手架工具的边界。


本篇属于系列《S06 · Java 工程化与组件》。扩展点 E-S06-009/010。InvalidClassException 为 JDK 21 实测。

S06-J02 · 序列化陷阱:版本兼容、循环引用与反序列化安全
https://www.clxhxhhr.top/posts/5262/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。