单例设计模式-反射攻击解决方案及原理
最后更新于:2022-08-15 13:34:31
继续使用饿汉式代码
public class Test {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class objectClass = HungrySingleton.class;
Constructor constructor = objectClass.getDeclaredConstructor();
// 把private方法设置为可访问
constructor.setAccessible(true);
HungrySingleton instance = HungrySingleton.getInstance();
HungrySingleton newInstance = (HungrySingleton) constructor.newInstance();
System.out.println(instance);
System.out.println(newInstance);
System.out.println(newInstance == instance);
}
}
结果
com.honor.design.pattern.creational.singleton.HungrySingleton@14ae5a5
com.honor.design.pattern.creational.singleton.HungrySingleton@7f31245a
false
可以看到,通过反射可以创建出一个新的实例。
修改饿汉式单例
public class HungrySingleton implements Serializable {
private final static HungrySingleton hungrySingleton;
static {
hungrySingleton = new HungrySingleton();
}
private HungrySingleton() {
if (hungrySingleton != null) {
throw new RuntimeException("单例构造器禁止反射调用");
}
}
public static HungrySingleton getInstance() {
return hungrySingleton;
}
private Object readResolve() {
return hungrySingleton;
}
}
运行结果
Caused by: java.lang.RuntimeException: 单例构造器禁止反射调用
at com.honor.design.pattern.creational.singleton.HungrySingleton.(HungrySingleton.java:15)
... 5 more
使用懒汉式单例模式测试
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class objectClass = LazySingleton.class;
Constructor constructor = objectClass.getDeclaredConstructor();
constructor.setAccessible(true);
// 顺序不同,结果不同
LazySingleton instance = LazySingleton.getLazySingleton();
LazySingleton newInstance = (LazySingleton) constructor.newInstance();
System.out.println(instance);
System.out.println(newInstance);
System.out.println(newInstance == instance);
}
同时修改私有化构造器
public class LazySingleton {
private static LazySingleton lazySingleton = null;
private LazySingleton() {
if (lazySingleton != null) {
throw new RuntimeException("单例构造器禁止反射调用");
}
}
public static synchronized LazySingleton getLazySingleton() {
if (lazySingleton == null) {
lazySingleton = new LazySingleton();
}
return lazySingleton;
}
}
先使用单例模式。在使用反射的结果
Caused by: java.lang.RuntimeException: 单例构造器禁止反射调用
at com.honor.design.pattern.creational.singleton.LazySingleton.(LazySingleton.java:9)
... 5 more
先使用反射,在使用单例
com.honor.design.pattern.creational.singleton.LazySingleton@14ae5a5
com.honor.design.pattern.creational.singleton.LazySingleton@7f31245a
false
修改方法,在私有构造器中添加逻辑
public class LazySingleton {
private static LazySingleton lazySingleton = null;
private boolean flag = true;
private LazySingleton() {
if (flag) {
//TODO 可以为任意复杂逻辑
flag = false;
} else {
throw new RuntimeException("单例构造器禁止反射调用");
}
}
public static synchronized LazySingleton getLazySingleton() {
if (lazySingleton == null) {
lazySingleton = new LazySingleton();
}
return lazySingleton;
}
}
因为使用反射,可以获取类的所有信息,所以即便添加逻辑,也不能阻止反射成功创建单例
public static void main(String[] args) throws NoSuchMethodException, NoSuchFieldException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class clazz = LazySingleton.class;
Constructor c = clazz.getDeclaredConstructor();
c.setAccessible(true);
LazySingleton o1 = LazySingleton.getLazySingleton();
Field flag = o1.getClass().getDeclaredField("flag");
flag.setAccessible(true);
flag.setBoolean(o1,true);
LazySingleton o2 = (LazySingleton) c.newInstance();
System.out.println(o1);
System.out.println(o2);
System.out.println(o1 == o2);
}
结果
反射攻击成功
com.honor.design.pattern.creational.singleton.LazySingleton@14ae5a5
com.honor.design.pattern.creational.singleton.LazySingleton@7f31245a
false
使用静态类懒汉模式
public class StaticInnerClassSingleton {
private StaticInnerClassSingleton(){
if (InnerClass.staticInnerClassSingleton != null) {
throw new RuntimeException("单例构造器禁止反射调用");
}
}
private static class InnerClass {
private static StaticInnerClassSingleton staticInnerClassSingleton = new StaticInnerClassSingleton();
}
public static StaticInnerClassSingleton getInstance() {
return InnerClass.staticInnerClassSingleton;
}
}
因为静态类懒汉模式,属于类加载的时候就创建了实例,所以可以预见结果是反射攻击失败
测试
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class objectClass = StaticInnerClassSingleton.class;
Constructor constructor = objectClass.getDeclaredConstructor();
constructor.setAccessible(true);
StaticInnerClassSingleton newInstance = (StaticInnerClassSingleton) constructor.newInstance();
StaticInnerClassSingleton instance = StaticInnerClassSingleton.getInstance();
System.out.println(instance);
System.out.println(newInstance);
System.out.println(newInstance == instance);
}
结果
Caused by: java.lang.RuntimeException: 单例构造器禁止反射调用
at com.design.pattern.creational.singleton.StaticInnerClassSingleton.init(StaticInnerClassSingleton.java:7)
... 5 more