考虑用序列化代理代替序列化实例
前提:使用Serializable接口会增加出错和出现安全问题的可能性(因为他导致实例要利用语言之外的机制来创建,而不是普通的构造器)
解决方案:使用序列化代理模式
- 为可序列化的类设计一个私有的静态嵌套类,精确的表示外围类的实例的逻辑状态,这个嵌套类就是序列化代理
- 嵌套类具有一个单独的构造器,参数类型就是外围类,这个构造器只从它的参数中复制数据(不进行一致性检查和保护性拷贝)
- 序列化的时候使用代理类
- 反序列化的时候创建外部类
public final class Period {
private final Date start;
private final Date end;
public Period(Date start, Date end) {
if (start.compareTo(end) > 0) {
throw new IllegalArgumentException(start + "after" + end);
}
this.start = start;
this.end = end;
}
public Date start() {
return start;
}
public Date end() {
return end;
}
/**
* 这个方法的存在导致序列化系统产生一个代理实例,替代原对象
* 序列化系统永远不会产生该类的实例
* @return
*/
private Object writeReplace() {
return new SerializationProxy(this);
}
private static class SerializationProxy implements Serializable {
private final Date start;
private final Date end;
SerializationProxy(Period p) {
this.start = p.start;
this.end = p.end;
}
/**
* 返回一个外围类的实例,反序列化的时候返回外围类的实例
* @return
*/
private Object readResovle() {
return new Period(start, end);
}
}
/**
* 添加该方法确保攻击者无法伪造,违反该类的约束条件
* @param stream
* @throws InvalidObjectException
*/
private void readObject(ObjectInputStream stream) throws InvalidObjectException {
throw new InvalidObjectException("Proxy required");
}
}
writeReplace方法:添加该方法后,该类在进行序列化的时候,序列化的对象是该方法返回的对象
readObject与writeObject方法:可以手动将static和transient的属性进行序列化
@Setter
@Getter
public class SessionDTO implements Serializable {
private transient int data;
private transient long activationTime;
public SessionDTO(int data) {
this.data = data;
this.activationTime = System.currentTimeMillis();
}
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject();
oos.writeInt(data);
System.out.println("session serialized");
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.defaultReadObject();
data = ois.readInt();
activationTime = System.currentTimeMillis();
System.out.println("session deserialized");
}
}
序列化代理方法可以阻止伪字节流的攻击,以及内部域的盗用攻击,这种方法允许Period的域为final的
结论:
- 局限:扩展性
- 开销大
- 要想稳健的将带有重要约束条件的对象序列化的时候,使用该方法最容易