如何防止单例模式被 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();
    }
}

运行结果如下:

Exception in thread "main" java.lang.NoSuchMethodException: sf.com.singleton.SingletonClass.<init>()
    at java.lang.Class.getConstructor0(Class.java:3082)
    at java.lang.Class.getDeclaredConstructor(Class.java:2178)
    at sf.com.singleton.TestMain.main(TestMain.java:10)

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

posted @ 2017-01-03 17:45  胡金水  阅读(2069)  评论(2编辑  收藏  举报