Java中仅实现Serializable无法加密,因其是无方法的标记接口,默认序列化会明文输出所有非transient非static字段;必须通过private writeObject/readObject钩子在序列化前后手动加解密敏感字段。

Java 中通过实现 Serializable 接口并重写 writeObject 方法,可以控制对象序列化过程,从而在写入字节流前对敏感字段加密。这不是“加密序列化协议”,而是利用序列化钩子(serialization hook)在标准流程中插入自定义逻辑。
为什么不能只靠实现 Serializable?
Serializable 是个标记接口,本身不提供任何方法。默认序列化会将所有非 transient、非 static 字段原样转为字节流,毫无安全性可言。要加密,必须干预序列化行为——writeObject 和 readObject 就是为此设计的私有钩子方法。
基本步骤:加密写 + 解密读
需成对实现 writeObject 和 readObject,否则反序列化会失败或数据错乱:
- 声明
private void writeObject(ObjectOutputStream out) throws IOException - 声明
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException - 在
writeObject中:先调用out.defaultWriteObject()序列化非敏感字段;再对敏感字段(如密码、token)加密后单独写入(例如用out.writeUTF(encrypted)) - 在
readObject中:先调用in.defaultReadObject()恢复普通字段;再手动读取加密内容(如String enc = in.readUTF()),解密后赋值给对应字段
关键细节与注意事项
实际使用时需注意几个易错点:
- 两个方法必须是
private、void返回、参数严格匹配,且不能被继承或重载——JVM 通过反射精确调用它们 - 加密密钥不能硬编码,也不应作为对象字段参与序列化(否则等于明文存储)。建议从外部安全源获取(如系统属性、环境变量、密钥管理服务)
- 使用强加密算法(如 AES/GCM),避免 ECB 模式;务必处理 IV(初始化向量)的序列化与还原(通常和密文一起写入)
- 若类有父类且父类字段需保护,不能依赖
defaultWriteObject——需显式调用super.writeObject(out)(前提是父类也支持该机制)
一个简化示例(仅示意流程)
假设有一个用户类,其中 password 需加密:
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private String username;
private transient String password; // 不参与默认序列化
<pre class="brush:php;toolbar:false;">private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject(); // 写 username 等非 transient 字段
String encrypted = encrypt(password); // 自定义加密方法
out.writeUTF(encrypted);
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject(); // 读 username
String encrypted = in.readUTF();
this.password = decrypt(encrypted); // 自定义解密
}
private String encrypt(String plain) { /* AES 加密实现 */ }
private String decrypt(String enc) { /* 对应解密 */ }
}
这种方式把加密逻辑封装在类内部,调用方无需感知——只要用 ObjectOutputStream 写,就自动加密;用 ObjectInputStream 读,就自动解密。但请记住:它不替代传输层加密(如 TLS)或存储层加密,仅是对 Java 序列化流的字段级保护。










