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

Posted 猿猿HHH

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单例模式如何防止反射攻击相关的知识,希望对你有一定的参考价值。

单例模式例子:

public class Single {
    private Single ()  {//私有化构造方法
    }
    private static volatile Single instance = null;
    public static synchronized Single getInstance() {
        if(instance==null){
            instance = new Single();//在此方法内创建对象
        }
        return instance;//返回所创建的对象
    }
}

以上代码若采用反射来创建对象就会翻车了!!
例子:

class Main{
    public static void main(String[] args) throws Exception {
        Class clazz = Single.class;
        Constructor constructor = clazz.getDeclaredConstructor();
        constructor.setAccessible(true);
        Single single = Single.getInstance();
        Single single1 = (Single) constructor.newInstance();
        System.out.println(single==single1);
    }
}

输出:
在这里插入图片描述
这方法如何解决呢?
暴力反射调取了类中的私有化构造器方法,而instance属性是静态的,所以可以判断instance是否为null,若不是null,则抛出异常警告

代码如下:

public class Single {
    private Single ()  {
        if(instance!=null){
            throw new RuntimeException("禁止使用反射调用!");
        }
    }
    private static volatile Single instance = null;
    public static synchronized Single getInstance() {
        if(instance==null){
            instance = new Single();
        }
        return instance;
    }
}

运行结果如下:
在这里插入图片描述
成功解决!

在这读者可能会有个问题,以上只使用了一次反射来调取构造方法创建对象,若两次创建对象都采用反射的方式呢?该如何解决?
例如

class Main{
    public static void main(String[] args) throws Exception {
        Class clazz = Single.class;
        Constructor constructor = clazz.getDeclaredConstructor();
        constructor.setAccessible(true);
        Single single1 = (Single) constructor.newInstance();
        Single single = (Single) constructor.newInstance();
        System.out.println(single==single1);
    }
}

很遗憾,这种情况下若要坚持采用懒汉式的单例模式是不能解决的。
只能够使用饿汉式的单例模式解决:

public class Single {
    private Single ()  {
        if(instance!=null){
            throw new RuntimeException("禁止使用反射调用!");
        }
    }
    private static volatile Single instance = new Single();
    public static Single getInstance() {
//        if(instance==null){
//            instance = new Single();
//        }
        return instance;
    }
}
class Main{
    public static void main(String[] args) throws Exception {
//        Single single1 = Single.getInstance();
//        Single single2 = Single.getInstance();
//        System.out.println(single1==single2);
        Class clazz = Single.class;
        Constructor constructor = clazz.getDeclaredConstructor();
        constructor.setAccessible(true);
//        Single single = Single.getInstance();
        Single single1 = (Single) constructor.newInstance();
        Single single = (Single) constructor.newInstance();
        System.out.println(single==single1);
    }
}

在这里插入图片描述

以上是关于单例模式如何防止反射攻击的主要内容,如果未能解决你的问题,请参考以下文章

如何防止JAVA反射对单例类的攻击?

如何避免反射和序列化破坏单例模式

单例模式--反射--防止序列化破坏单例模式

单例模式-反射攻击的解决方案及原理分析

单例模式的几种写法

单例模式总结