序列化代理模式-readResolve()和writeReplace()

时间:2020-01-09 10:35:19  来源:igfitidea点击:

在本文中,我们将讨论序列化代理模式,这是通过序列化代理对象来序列化对象的一种更安全的方法。

需要序列化代理模式

Java中的序列化确实存在一些问题,例如

1.我们对整个序列化过程没有太多控制权。随着时间的推移更改类还会导致与序列化对象的兼容性问题。

2.在序列化中,实际对象被序列化,这增加了安全风险。任何攻击者都可以从序列化状态重建实际对象,或者更改流以操纵对象数据。

使用序列化代理模式,我们可以保护序列化过程。代理对象而不是实际对象被序列化。为了获得代理对象,我们需要在一个真实的类中创建一个静态内部类,该类也实现了Serializable接口,并且具有与实际类相同的字段。

Java中的readResolve()和writeReplace()方法

我们需要实现两个方法readResolve()和writeReplace()才能正确实现序列化代理模式。

writeReplace()–此方法由想要将代理对象写入流的Serializable类实现。

ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;

readResolve()–从流中读取实例的实例时需要返回替换的类应实现此方法。从流中读取代理对象时,必须以序列化代理模式返回实际对象,因此必须在代理类中实现此方法。

ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;

序列化代理模式Java示例

这是一个其对象必须使用序列化代理模式进行序列化的类,因此它将具有静态内部类,并且也实现了readResolve()和writeReplace()方法。

import java.io.Serializable;

public class Employee implements Serializable{
  private static final long serialVersionUID = -8690951276691191306L;
  private String name;
  private String dept;
  private int salary;
  private int age;
  Employee(String name, String dept, int salary, int age){
    this.name = name;
    this.dept = dept;
    this.salary = salary;
    this.age = age;
  }
	
  //writeReplace method for the proxy pattern	
  private Object writeReplace() {
    System.out.println("In writeReplace() method");
    return new EmployeeProxy(this);
  }

  private void readObject(ObjectInputStream ois) throws InvalidObjectException{
    throw new InvalidObjectException("Use proxy class");
  }

  private static class EmployeeProxy implements Serializable{
    private String name;
    private String dept;
    private int salary;
    private int age;
    EmployeeProxy(Employee emp){
      this.name = emp.name;
      this.dept = emp.dept;
      this.salary = emp.salary;
      this.age = emp.age;
    }
    // readResolve method in Proxy class
    private Object readResolve() {
      System.out.println("In readResolve() method");
      return new Employee(name, dept, salary, age);
    } 
  }	
  // setters and getters
}

我们应注意的一些重要事项是:

  1. EmployeeProxy类定义为静态内部类。代理类还实现了Serializable接口。
  2. EmployeeProxy类的构造函数将Employee对象作为参数,并使用它创建一个代理对象。在需要时创建代理对象时,我们也可以加密数据以使其更安全。
  3. Employee类实现了writeReplace()方法,该方法返回代理类的实例。
  4. EmployeeProxy类实现readResolve()方法并返回Employee对象。
    5.作为添加的预防措施,还应实现readObject()方法以引发异常。这样就没有机会从流中构造Employee对象。

这是带有用于序列化和反序列化对象的方法的类。

public class SerializationDemo {

  public static void main(String[] args) {
    Employee emp = new Employee("Ryan", "IT", 7500, 35);
    final String fileName = "F:\theitroad\emp.ser";
    serialzeObject(emp, fileName);
    
    /// Do null check here
    Employee e = (Employee)deSerializeObject(fileName);
    System.out.println("Name- " + e.getName());
    System.out.println("Dept- " + e.getDept());
    System.out.println("Salary- " + e.getSalary());
    System.out.println("Age- " + e.getAge());
  }	
	
  // Method to serialize object 
  public static void serialzeObject(Object obj, String fileName) { 
    try(ObjectOutputStream outStream = new ObjectOutputStream(new FileOutputStream(fileName))){
      outStream.writeObject(obj); 
    } catch (IOException e) { 
      // TODO Auto-generated
      e.printStackTrace(); 
    } 
  }
	 	
  // Method to deserialize object
  public static Object deSerializeObject(String fileName){
    Object obj = null;
    try(ObjectInputStream inStream = new ObjectInputStream(new FileInputStream(fileName))){
      obj = inStream.readObject();	 			
    } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (ClassNotFoundException e) {
      System.out.println("No class found for serialization");
      e.printStackTrace();
    }
    return obj;
  }
}

输出:

In writeReplace() method to serialize proxy
In readResolve() method to get actual object
Name- Ryan
Dept- IT
Salary- 7500
Age- 35