(单例设计模式之一)饿汉式的反射与反序列化漏洞
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了(单例设计模式之一)饿汉式的反射与反序列化漏洞相关的知识,希望对你有一定的参考价值。
1.闲话少说,直接上代码。
import java.io.Serializable;
//饿汉式
public class Singleton01 implements Serializable{
//1.私有的属性
private static Singleton01 instance=new Singleton01();
//2.私有的构造器
private Singleton01(){}
//3.共有的get()方法
public static Singleton01 getInstance(){//这里不需要加同步块synchronized
return instance;
}
}
为什么不需要加同步块?这里插播一条短消息
1. 饿汉式是天然的线程安全的,因此不需要加线程锁。饿汉式之所以是天然的线程安全,是因为:
一个字节码文件被加载到内存会经历以下过程:
1、链接(1、验证2、准备3、解析)
2、初始化
3、使用
4、卸载
初始化是执行类构造器<clinit>()方法的过程。类构造器<clinit>()方法是由编译器自动收集类中的所有类变量的赋 值动作和静态语句块(static)中的语句合并产生的。在这个过程中instance已经被实例化了。并且虚拟机JVM会保证一个类 的<clinit>()方法在多线程的环境中被正确加锁和同步。因此他是天然线程安全的。
2.下面代码分析如何使用反射破解懒汉式:
public class Singletons_Test_02 {
public static void main(String[] args) throws Exception {
Class<Singleton01> clazz = (Class<Singleton01>) Class.forName("com.shc.singleton.Singleton01");
Constructor<Singleton01> c = clazz.getDeclaredConstructor(null);//获得无参构造器
c.setAccessible(true);//跳过权限检查,访问私有的构造器
Singleton01 s3 = c.newInstance();
Singleton01 s4 = c.newInstance();
System.out.println(s3);
System.out.println(s4);
}
}
此时输出s3与s4的哈希值是相等的。代表不是同一个对象,饿汉式单例通过反射破解完成。
3.下面分析饿汉式如何避免反射漏洞?
public class Singleton1 {
//1.私有的属性
private static Singleton1 instance=new Singleton1();
//2.私有的构造器
private Singleton1(){
if (null != instance) {
throw new RuntimeException();
}
}
//3.共有的get()方法
public static Singleton1 getInstance(){//天然的线程安全,不需要加同步块,因此调用效率高
return instance;
}
}
在私有的构造器中加一个判断,判断要创建的对象是否存在,如果不存在再创建,已存在就抛出运行时异常。
4.下面利用序列化与反序列化漏洞破解饿汉式单例
public class Singletons_Test_03 {
public static void main(String[] args) throws Exception {
Singleton01 s1 = Singleton01.getInstance();
Singleton01 s2 = Singleton01.getInstance();
System.out.println(s1);
System.out.println(s2);
//序列化
FileOutputStream fos = new FileOutputStream("../a.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(s1);
oos.close();
fos.close();
//反序列化
ObjectInputStream ois =new ObjectInputStream(new FileInputStream("../a.txt"));
Singleton01 s3 = (Singleton01) ois.readObject();
System.out.println(s3);
}
}
5.破解饿汉式反序列化漏洞
public class Singleton11 implements Serializable{
//1.私有的属性
private static Singleton11 instance=new Singleton11();
//2.私有的构造器
private Singleton11(){}
//3.共有的get()方法
public static Singleton11 getInstance(){
return instance;
}
//反序列化时(加这个方法可以防止反序列化漏洞)
private Object readResolve() throws ObjectStreamException{
return instance;
}
}
以上demo较为简单,不做深入研究,设计模式体验重在应用。
以上是关于(单例设计模式之一)饿汉式的反射与反序列化漏洞的主要内容,如果未能解决你的问题,请参考以下文章