`
liyebing
  • 浏览: 56847 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

序列化揭秘(二)

阅读更多

     反序列化的时候,并不是调用类的构造函数来实现实例的构建,而是通过一种语言之外的对象创建机制来构造对象实例。。从底层源码来看,生成实例时调用了java.reflect.Constructor 的newInstance()方法:

 

// 用反射生成实例
public T newInstance(Object ... initargs)
throws InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException {
//。。。。。。此处省略。。。。。。。
return (T) constructorAccessor.newInstance(initargs);
}

 

     而最后实际上是调用了ConstructorAccessor 的newInstance()方法,而再往下,ConstructorAccessor 调用了本地方法。

 

   由此产生了两个问题:

   (一)、通过序列化可以任意创建实例,不受任何限制。那么如何确保单例类反序列化的时候满足单例特性

   (二)、由于不调用类自己的构造器,可能通过构造器来保证的一些限制条件就很难满足。比如,有两个参数max,min,构造 时必须满足max>min

 

 

  下面提供了解决上述两个问题的方法。

  先看看单例模式是如何被序列化蹂躏的,再来看看我们解决方案。

 

// 单例类
public class ASingleton implements Serializable{
private static final long serialVersionUID = -4647546107954516623L;
private static final ASingleton INSTANCE = new ASingleton();
private ASingleton(){
System.out.println("construct a ASingleton");
}
public static ASingleton getInstance(){
return INSTANCE;
}
}


// 单例测试类
public class TestSingleton {
public static void main(String[] args) throws IOException, ClassNotFoundException{
ASingleton s = ASingleton.getInstance();
SerialUtils.writeObject(s, "asingleton.byte");
System.out.println(s);
SerialUtils.readObject("asingleton.byte");
}
}
 

   测试方法会打印出序列化前对象,和反序列化后的对象,我们看看是否是同一个实例

   construct a ASingleton
   file size:51bytes
   test.serial.resolve.ASingleton@3dfeca64
   read form asingleton.byte get test.serial.resolve.ASingleton@457471e0

 

   可以看出来不是同一个实例。这个问题的解决方案是:可以通过覆写readResolve 这个方法来实现,readResolve 会在反序列化时调用,当从流中获取信息重建对象生成实例后,将会调用这个方法,将生成的实例替换成我们想要的实例,看代码:

 

 

// 单例类覆写了readResolve
public class ASingleton implements Serializable{
// 此处省略100 个字
private Object readResolve(){
System.out.println("resolve instance");
return INSTANCE;}
}
 

   可以看出,我们覆写了这个方法,并且将那个唯一的实例(INSTANCE)返回,从而保证了单例。看
看运行结果证明我们的想法:

construct a ASingleton
file size:51bytes
test.serial.resolve.ASingleton@3dfeca64
resolve instance
read form asingleton.byte get test.serial.resolve.ASingleton@3dfeca64

 

可以看出是一个实例,并且readResolve 也被调用了。
到底替换实例的过程是怎样的呢,我们在jdk 源码中寻求答案:

 

// 源码中是如何使用readResolve的
private Object readOrdinaryObject(boolean unshared) throws IOException{
// ...
ObjectStreamClass desc = readClassDesc(false);
desc.checkDeserialize();
Object obj;
try {
obj = desc.isInstantiable() ? desc.newInstance() : null;
}
// ....
if (obj != null && handles.lookupException(passHandle) == null && desc.hasReadResolveMethod()) {
Object rep = desc.invokeReadResolve(obj);
// …
if (rep != obj) {
handles.setObject(passHandle, obj = rep);
}
}
return obj;
}

    从代码可以看出,原理是这样的:
   (1)首先会从流中读取信息,重建对象obj
   (2) 判断是否覆写了readResolve 方法,如果没有覆写则直接返回obj
   (3) 如果覆写了readResolve 方法,则调用readResolve 方法返回实例rep,如果rep与obj不相等,则用rep 替换obj,最后返回obj替换过程就是这样的,至此,单例问题解决了。

 

 

  最后看看第二个问题如何解决。第二个问题的解决思路有两个,一个是覆写readObject 并且在方法里校验我们的约束信息,如果有问题就抛出异常。代码如下:

// 利用readObject 来防攻击
public class Range implements Serializable{
// 此出省略若干字
private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException{
s.defaultReadObject();
int max = this.max;
int min = this.min;
if(max < min){
throw new IllegalArgumentException("max must bigger than min");
}
}
// ….
}

 

   另一个解决思路是让对象实现ObjectInputValidation中的public void validateObject() throws InvalidObjectException 方法。在该方法中校验反序列化之后得实例必须满足的某些特性。如不满足则抛出相应的异常。代码如下:

 

     序列化类的代码如下:

 

package zheyuan.experiment4.com;

import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectInputValidation;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

public class ValidationPerson implements Serializable,ObjectInputValidation {
	
	private static final long serialVersionUID = 5497993111607600169L;
	private String firstName;
	private String lastName;
	
	private List<String> lis=new ArrayList<String>();
	
	public ValidationPerson(){
		lis.add("validationPerson");
	}

	public String getFirstName() {
		return firstName;
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	public List<String> getLis() {
		return lis;
	}

	public void setLis(List<String> lis) {
		this.lis = lis;
	}

	public void validateObject() throws InvalidObjectException {
		//这里的这个异常是肯定会抛出来的,只是为了演示该方法的使用
		if(this.lis.contains("validationPerson")){
			throw new InvalidObjectException("validationPerson缺失!");
		}
	}
	
	//注册验证对象
	private void readObject(ObjectInputStream in)
	   throws IOException, ClassNotFoundException {
	    in.registerValidation(this, 1);
	    in.defaultReadObject();
	  }
}

 

   测试类的代码如下:

 

package zheyuan.experiment4.com;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;

public class ValidationSerializableTest {

	/**
	 * 反序列化的时候通过继承实现接口ObjectInputValidation中的方法校验反序列化之后类的某些特性
	 * @param args
	 */
	public static void main(String[] args) throws Throwable{

		ValidationPerson vp=new ValidationPerson();
		//序列化
		OutputStream out=new FileOutputStream("validation_sec");
		ObjectOutputStream oos=new ObjectOutputStream(out);
        oos.writeObject(vp);
		
        //反序列化
        InputStream ins=new FileInputStream("validation_sec");
        ObjectInputStream ois=new ObjectInputStream(ins);
        ValidationPerson vpNew=(ValidationPerson)ois.readObject();
        System.out.println(vpNew.getLis().get(0));
	}

}

 

   运行结果如下:

 

 

Exception in thread "main" java.io.InvalidObjectException: validationPerson缺失!
	at zheyuan.experiment4.com.ValidationPerson.validateObject(ValidationPerson.java:50)
	at java.io.ObjectInputStream$ValidationList$1.run(ObjectInputStream.java:2207)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.io.ObjectInputStream$ValidationList.doCallbacks(ObjectInputStream.java:2203)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:358)
	at zheyuan.experiment4.com.ValidationSerializableTest.main(ValidationSerializableTest.java:27)
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

分享到:
评论

相关推荐

    《逆向工程揭秘》中文版 共3部分.part1

    这几个附录远远超出了简单的汇编语言参考向导,讲述了公共代码段(common code fragments)和常用编译器对几种典型的代码序列表现出来的编译器习性(complier idioms),并介绍了识别和破解它们的方法。

    Python 3.8 新功能大揭秘【新手必学】

    现在Python 3.8已经进入官方的beta阶段,这个版本带来了许多语法改变、内存共享、更有效的序列化和反序列化、改进的字典和更多新功能。 好了,正文开始,一起看看吧! Python 3.8 是 Python 编程语言的最新主要版本...

    CLR.via.C#.(中文第3版)(自制详细书签)Part1

    11.1.2 第二步:定义事件成员 11.1.3 第三步:定义负责引发事件的方法来通知事件的登记对象 11.1.4 第四步:定义方法将输入转化为期望事件 11.2 编译器如何实现事件 11.3 设计侦听事件的类型 11.4 显式实现...

    CLR.via.C#.(中文第3版)(自制详细书签)

    24.5 控制序列化/反序列化的数据 24.5.1 如何在基类没有实现ISerializable的前提下定义一个实现它的类型 24.6 流上下文 24.7 将类型序列化为不同的类型以及将对象反序列化为不同的对象 24.8 序列化代理 24.8.1 ...

    CLR.via.C#.(中文第3版)(自制详细书签)Part3

    11.1.2 第二步:定义事件成员 11.1.3 第三步:定义负责引发事件的方法来通知事件的登记对象 11.1.4 第四步:定义方法将输入转化为期望事件 11.2 编译器如何实现事件 11.3 设计侦听事件的类型 11.4 显式实现...

    CLR.via.C#.(中文第3版)(自制详细书签)Part2

    11.1.2 第二步:定义事件成员 11.1.3 第三步:定义负责引发事件的方法来通知事件的登记对象 11.1.4 第四步:定义方法将输入转化为期望事件 11.2 编译器如何实现事件 11.3 设计侦听事件的类型 11.4 显式实现...

    《软件系统架构与开发环境》第二章源代码-by 南邮-陈杨

    2.4.2 Java的对象序列化技术 98 2.4.3 Java的异常处理技术 101 2.4.4 Java线程的并发控制 104 2.4.5 Java的集合类 105 2.5 Visual C++的架构相关技术 107 2.5.1 Windows API的窗口技术与消息处理技术 107 ...

    2023数据科学峰会(脱敏)PPT汇总.zip

    数据中台与数字化转型论坛 数据服务中台建设实践 从A最佳实践到瓴羊数据治理 3、因果推断论坛 探究产品策略优化的长期影响 序列数据的因果推断在仓储管理的应用 AIGC与因果推断的双向赋能 4、用户增长与运营论坛 ...

    ASP.NET4高级程序设计(第4版) 3/3

    12.3 序列化 423 12.4 总结 426 第13章 LINQ 427 13.1 LINQ基础 427 13.1.1 延迟执行 429 13.1.2 LINQ是如何工作的 429 13.1.3 LINQ表达式 430 13.1.4 揭秘LINQ表达式 436 13.2 LINQ to DataSet ...

    ASP.NET4高级程序设计第4版 带目录PDF 分卷压缩包 part1

    12.3 序列化 12.4 总结 第13章 LINQ 13.1 LINQ基础 13.1.1 延迟执行 13.1.2 LINQ是如何工作的 13.1.3 LINQ表达式 13.1.4 揭秘LINQ表达式 13.2 LINQ to DataSet 13.2.1 强类型的DataSet 13.2.2...

    asp.net知识库

    泛型的序列化问题 .NET 2.0 泛型在实际开发中的一次小应用 C#2.0 Singleton 的实现 .Net Framwork 强类型设计实践 通过反射调用類的方法,屬性,字段,索引器(2種方法) ASP.NET: State Server Gems 完整的动态加载/卸载...

Global site tag (gtag.js) - Google Analytics