利用反射機制破壞單例(2)
阿新 • • 發佈:2018-11-20
前言
在部落格上看到一篇《防止單例模式被JAVA反射攻擊》的文章,通過一個靜態布林變數記錄下單例類是否是第一次初始化,然後在建構函式內丟擲異常來防止反射破壞。看起來合情合理,但細想,通過反射來修改那個靜態變數,再呼叫建構函式進行例項化,同樣可以破壞。按照上面的原理,我換了另一種方式實現單例類,並進行破壞。下面進行程式碼演示。
程式碼實現
單例類:
public class SingletonProPro {
private static SingletonProPro instance;
private SingletonProPro(){
if (instance!=null){
throw new RuntimeException("單例類已攔截入侵");
}
}
public static SingletonProPro getInstance(){
if(instance==null){
synchronized(SingletonProPro.class){
if(instance==null){
instance=new SingletonProPro();
}
}
}
return instance;
}
}
攻擊程式碼:
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public class Main {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
//通過呼叫getSingleton()方式獲取物件
SingletonProPro instance1 = SingletonProPro.getInstance();
//通過反射方式獲取物件
Class singletonProClass = instance1.getClass();
SingletonProPro instance2 = null;
//修改instance為null
Field flag = singletonProClass.getDeclaredField("instance");
flag.setAccessible(true);
System.out.println(flag.get(instance1));
flag.set(instance1,null);
System.out.println(flag.get(instance1));
Constructor<?> constructor = singletonProClass.getDeclaredConstructor();//獲取當前Class所表示類中指定的一個的構造器,和訪問許可權無關
constructor.setAccessible(true); //設定私有方法的可訪問(切記,這裡必須設定,否則會丟擲下圖的異常)
instance2 = (SingletonProPro) constructor.newInstance();
if(instance1==instance2){
System.out.println("相等");
}else {
System.out.println("不相等");
}
}
}
輸出結果:
[email protected]14ae5a5
null
不相等
防止反射破壞單例
最簡單的方法就是用static final來修飾instance並初始化:
public class SingletonPro {
private static final SingletonPro instance = new SingletonPro();
private SingletonPro(){
if(instance!=null){
throw new RuntimeException("單例模式被侵犯!");
}
}
public static SingletonPro getInstance(){
return instance;
}
}