zoukankan      html  css  js  c++  java
  • 通过反射机制、序列化反序列化破解单例模式

    一、懒汉式单例

    package edu.aeon.model.singleton;
    /**
     * 单例模式:懒汉式(类装载及创建)
     * 步骤:
     *     1.构造器私有化(防止外部new)
     *     2.提供静态的、私有的代表该对象的实例的空引用
     *     3.提供静态的、公开的函数(方法)供外部访问
     * @author aeon
     */
    public class LazySingleton {
        //1.构造器私有化(防止外部new)
        private LazySingleton(){}
        //2.提供静态的、私有的代表该对象的实例的空引用(调用时才给值)
        private static LazySingleton lazySingletonInstance=null;
        //3.提供静态的、公开的函数(方法)供外部访问应用该模式唯一实例
        public static LazySingleton getLazySingletonInstance(){
            if(null==lazySingletonInstance){
                lazySingletonInstance=new LazySingleton();
            }
            return lazySingletonInstance;
        }
    }

    二、破解单例模式(除枚举)

      2.1通过反射机制来破解上面提供的懒汉式单例  

    package edu.aeon.model.singleton.crack;
    
    import java.lang.reflect.Constructor;
    
    import edu.aeon.model.singleton.LazySingleton;
    
    /**
     * [说明]:通过反射机制破解单例模式(除枚举)
     * 步骤:
     *     1.加载单例类
     *     2.通过反射获得无参构造器
     *     3.解除本类访问单例类私有属性的限制(跳过权限的检查)。
     *     4.通过反射获得的无参构造器来获得实例
     *     5.对比获得实例是否为同一对象
     * @author aeon
     *
     */
    public class CrackSingletonByReflex {
    
        public static void main(String[] args) {
            System.out.println("破解前:"+(LazySingleton.getLazySingletonInstance()==LazySingleton.getLazySingletonInstance()?"是同一对象!":"不是同一对象!"));
            try {
                //1.加载单例类
                Class<LazySingleton> clazz = (Class<LazySingleton>) Class.forName("edu.aeon.model.singleton.LazySingleton");
                //2.通过反射获得无参构造器
                Constructor<LazySingleton> constructor= clazz.getDeclaredConstructor();
                //3.解除本类访问单例类私有属性的限制(跳过权限的检查)。
                constructor.setAccessible(true);
                //4.通过反射获得的无参构造器来获得实例
                LazySingleton lazySingleton1=(LazySingleton) constructor.newInstance();
                LazySingleton lazySingleton2=(LazySingleton) constructor.newInstance();
                //5.对比获得实例是否为同一对象
                System.out.println("破解后:"+(lazySingleton1==lazySingleton2?"是同一对象!":"不是同一对象!"));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
    }

      运行结果截图如下:

      

       很明显的看到通过反射机制可以破坏这种单例模式的本质。那么如何防止呢?

        2.1.1防止反射机制破解单例模式

    package edu.aeon.model.singleton;
    /**
     * 单例模式:懒汉式(类装载及创建)
     * 步骤:
     *     1.构造器私有化(防止外部new)
     *     2.提供静态的、私有的代表该对象的实例的空引用
     *     3.提供静态的、公开的函数(方法)供外部访问
     * @author aeon
     */
    public class LazySingleton {
        //1.构造器私有化(防止外部new)
        private LazySingleton(){
            if(null!=lazySingletonInstance){
                throw new RuntimeException("单例模式不允许外界使用构造器!");
            }
        }
        //2.提供静态的、私有的代表该对象的实例的空引用(调用时才给值)
        private static LazySingleton lazySingletonInstance=null;
        //3.提供静态的、公开的函数(方法)供外部访问应用该模式唯一实例
        public static LazySingleton getLazySingletonInstance(){
            if(null==lazySingletonInstance){
                lazySingletonInstance=new LazySingleton();
            }
            return lazySingletonInstance;
        }
    }

      我们再次运行通过反射机制破解单例模式程序、可以发现:

      


      原理:当存在这个单例的唯一实例的时候,通过反射机制跳过权限检查再次使用构造器构造对象的时候,通过抛出运行时异常的形式来阻止外界使用构造器来构造实例。

      2.2通过序列化/反序列化破解单例模式  

    package edu.aeon.model.singleton.crack;
    
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import edu.aeon.model.singleton.LazySingleton;
    
    /**
     * [说明]:通过序列化/反序列化破解单例模式(除枚举) 
     * 步骤:
     *  1.序列化单例类对象实例 
     *  2.通过刚才生成的序列化对象文件反序列化生成对象
     * @author aeon
     *
     */
    public class CrackSingletonBySerialize {
    
        public static void main(String[] args) {
            LazySingleton lazySingleton1 = LazySingleton.getLazySingletonInstance();
            LazySingleton lazySingleton2 = LazySingleton.getLazySingletonInstance();
            System.out.println((lazySingleton1 == lazySingleton2 ? "破解前是同一对象!" : "破解前不是同一对象!"));
            // 1.序列化单例类对象实例
            FileOutputStream fileOutputStream = null;
            ObjectOutputStream objectOutputStream = null;
            FileInputStream fileInputStream = null;
            ObjectInputStream objectInputStream = null;
            try {
                fileOutputStream = new FileOutputStream("b:/lazySingletonInstance.txt");
                objectOutputStream = new ObjectOutputStream(fileOutputStream);
                objectOutputStream.writeObject(lazySingleton1);
                System.out.println("序列化对象成功!");
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    objectOutputStream.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            // 2.通过刚才生成的序列化对象文件反序列化生成对象
            try {
                fileInputStream = new FileInputStream("b:/lazySingletonInstance.txt");
                objectInputStream = new ObjectInputStream(fileInputStream);
                LazySingleton lazySingleton3 = (LazySingleton) objectInputStream.readObject();
                System.out.println((lazySingleton1 == lazySingleton3 ? "破解后是同一对象!" : "破解后不是同一对象!"));
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    objectInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
    
        }
    }

      运行结果截图如下:

      

      2.2.1如何防止序列化/反序列化破解单例模式   

    package edu.aeon.model.singleton;
    
    import java.io.Serializable;
    
    /**
     * 单例模式:懒汉式(类装载及创建)
     * 步骤:
     *     1.构造器私有化(防止外部new)
     *     2.提供静态的、私有的代表该对象的实例的空引用
     *     3.提供静态的、公开的函数(方法)供外部访问
     * @author aeon
     */
    public class LazySingleton implements Serializable {
        //1.构造器私有化(防止外部new)
        private LazySingleton(){
            if(null!=lazySingletonInstance){
                throw new RuntimeException("单例模式不允许外界使用构造器!");
            }
        }
        //2.提供静态的、私有的代表该对象的实例的空引用(调用时才给值)
        private static LazySingleton lazySingletonInstance=null;
        //3.提供静态的、公开的函数(方法)供外部访问应用该模式唯一实例
        public static LazySingleton getLazySingletonInstance(){
            if(null==lazySingletonInstance){
                lazySingletonInstance=new LazySingleton();
            }
            return lazySingletonInstance;
        }
        /**
         * 反序列化时通过该默认方法返回对象
         * @return
         */
        private  Object readResolve(){
            return lazySingletonInstance;
        }
    }

      我们再运行2.2代码、运行结果截图:

        


     原理:如果反序列化时定义了readResolve()方法、则会通过该方法返回对象的实例,而不是再序列化一个新对象返回、所以能保持返回的是同一对象的实例。

     这样就解决了单例模式中反射机制、序列化/反序列化漏洞。

    如有任何疑问可联系邮箱: 给我发邮件、或直接联系QQ:1584875179 || 点返回首页

  • 相关阅读:
    python课堂练习
    Python爬虫学习笔记(九)——Selenium的使用
    Python爬虫学习笔记(八)——智高考数据爬取
    Python爬虫学习笔记(七)——Ajax
    Python爬虫学习笔记(六)——BeautifulSoup和pyquery的使用
    Python爬虫学习笔记(五)——XPath的使用
    Python爬虫学习笔记(四)——猫眼电影Top100
    Python爬虫学习笔记(三)——正则表达式
    Python爬虫学习笔记(二)——requests库的使用
    Python爬虫学习笔记(一)——urllib库的使用
  • 原文地址:https://www.cnblogs.com/aeon/p/10212065.html
Copyright © 2011-2022 走看看