Java序列化


RMI中对数据对象的序列化采用的是Java序列化。而目前主流的微服务框架却几乎没有用到Java序列化,SpringCloud用的是Json序列化,Dubbo虽然兼容了Java序列化,但默认使用的是Hessian序列化。

Java序列化的缺陷

1.无法跨语言

2.易被攻击

攻击者可以创建循环对象链,然后将序列化后的对象传输到程序中反序列化,这种情况会导致hashCode方法被调用次数呈次方爆发式增长, 从而引发栈溢出异常。例如下面这个案例就可以很好地说明。

Set root = new HashSet();
Set s1 = root;
Set s2 = new HashSet();
for (int i = 0; i < 100; i++) {
Set t1 = new HashSet();
Set t2 = new HashSet();
t1.add("foo"); //使t2不等于t1
s1.add(t1);
s1.add(t2);
s2.add(t1);
s2.add(t2);
s1 = t1;
s2 = t2;
}

解决

  1. 通过反序列化对象白名单来控制反序列化对象,可以重写resolveClass方法,并在该方法中校验对象名字。

  2. 使用 protobuf,JSON

配图

protobuf 序列化

这是一个使用单例模式实现的类,如果我们将该类实现Java的Serializable接口,它还是单例吗?如果要你来写一个实现了Java的Serializable接口的单例,你会怎么写呢?

public class Singleton implements Serializable{

private final static Singleton singleInstance = new Singleton();

private Singleton(){}

public static Singleton getInstance(){
   return singleInstance; 
}

}

导致这个问题的原因是序列化中的readObject会通过反射,调用没有参数的构造方法创建一个新的对象。

所以我们可以在被序列化类中重写readResolve方法。

private Object readResolve(){ return singleInstance; }