如何防止单例模式被 JAVA 反射攻击

如何防止单例模式被 JAVA 反射攻击

package sf.com.singleton;

public class Demo {
    
    private static boolean flag = true;
    
    private  Demo() {
        System.out.println("flag==" + flag);
    }
    
    private static class SingletonHolder{
        private static final Demo INSTANCE = new Demo();
    }
    
    public static Demo getInstance(){
        return SingletonHolder.INSTANCE;
    }
    
    public void deSomethingElse(){
        
    }
}



package sf.com.singleton;

import java.lang.reflect.Constructor;

public class DemoRelectAttack {

    public static void main(String[] args){
        try {
            Class<Demo> classType = Demo.class;
            Constructor<Demo> constructor = classType.getDeclaredConstructor(null);
            //取消java的权限控制检查
            constructor.setAccessible(true);
            //下面两个都可以访问私有构造器
            Demo demo1 = (Demo) constructor.newInstance(); 
            Demo demo2 = Demo.getInstance();
            System.out.println(demo1 == demo2);
        } catch (Exception e) {
            e.printStackTrace();
        }
        
    }
}

运行结果:

可以看到,通过反射获取构造函数,然后调用setAccessible(true)就可以调用私有的构造函数,所有e1和e2是两个不同的对象。

如果要抵御这种攻击,可以修改构造器,让它在被要求创建第二个实例的时候抛出异常。

修改后的代码:

package sf.com.singleton;

public class DemoModify {
    
    private static boolean flag = true;
    
    private  DemoModify() {
        synchronized (DemoModify.class) {
            if (flag == false) {
                flag = !flag;
            }else{
                throw new RuntimeException("单例模式被侵犯!");
            }
        }
    }
    
    private static class SingletonHolder{
        private static final DemoModify INSTANCE = new DemoModify();
    }
    
    public static DemoModify getInstance(){
        return SingletonHolder.INSTANCE;
    }
    
    public void deSomethingElse(){
        
    }
}

package sf.com.singleton;

import java.lang.reflect.Constructor;

public class DemoRelectAttack {

    public static void main(String[] args){
        try {
            Class<DemoModify> classType = DemoModify.class;
            Constructor<DemoModify> constructor = classType.getDeclaredConstructor(null);
            //取消java的权限控制检查
            constructor.setAccessible(true);
            //下面两个都可以访问私有构造器
            DemoModify demo1 = (DemoModify) constructor.newInstance(); 
            DemoModify demo2 = DemoModify.getInstance();
            System.out.println(demo1 == demo2);
        } catch (Exception e) {
            e.printStackTrace();
        }
        
    }
}
 

运行结果如下:

可以看到,成功的阻止了单例模式被破坏。

 从JDK1.5开始,实现Singleton还有新的写法,只需编写一个包含单个元素的枚举类型。推荐写法:

package sf.com.singleton;

public enum SingletonClass {
    
    INSTANCE;
    
    public void test(){
        System.out.println("The Test!");
    }
}


package sf.com.singleton;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class TestMain {

    public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        Class<SingletonClass> classType = SingletonClass.class;
        Constructor<SingletonClass> constructor =(Constructor<SingletonClass>)  classType.getDeclaredConstructor();
        constructor.setAccessible(true);
        constructor.newInstance();
    }
}

运行结果如下:

 
 

由此可见这种写法也可以防止单例模式被“攻击”。

2、第二种方法简单直接

  private Singleton() {
        if(singleton!=null){
            throw new RuntimeException();
        }
    }

public class Demo {
    public static void main(String[] args) throws Exception {
        Singleton singletonOne = Singleton.getSingleton();
        Singleton singletonTwo = Singleton.getSingleton();
        System.out.println("One和Two是否是同一个实例? " + (singletonOne == singletonTwo));
        //利用反射来破坏单例模式
        Class clazz = Class.forName("com.jiagouedu.Singleton");
        Constructor constructor = clazz.getDeclaredConstructor(null);
        constructor.setAccessible(true);
        Singleton singletonThree = (Singleton) constructor.newInstance(null);
        System.out.println("One和three是否是同一个实例? "+(singletonOne==singletonThree));
    }
}
//双重检测锁模式
class Singleton {
    private Singleton() {
        if(singleton!=null){
            throw new RuntimeException();
        }
    }

    static Singleton singleton;

    public static Singleton getSingleton() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

猜你喜欢

转载自blog.csdn.net/tyyking/article/details/84337136