您当前的位置: 首页 >  Java

小志的博客

暂无认证

  • 0浏览

    0关注

    1217博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

java设计模式——单例模式中反射攻击及解决方案

小志的博客 发布时间:2020-05-31 22:30:33 ,浏览量:0

目录
  • 一、饿汉式的反射攻击及解决方法
      • 1.1、通过反射机制的方式和通过类名.方法名的方式分别获取对象,示例如下:
      • 1.2、饿汉式单例模式反射攻击的解决方案
  • 二、静态内部类的反射攻击及解决方法
      • 2.1、通过反射机制的方式和通过类名.方法名的方式分别获取对象,示例如下:
      • 2.2、静态内部类的反射攻击解决方案
  • 三、懒汉式的反射攻击及解决方法
      • 3.1、通过反射机制的方式和通过类名.方法名的方式分别获取对象,示例如下:
      • 3.2、懒汉式无法解决反射攻击
  • 四、结论

一、饿汉式的反射攻击及解决方法 1.1、通过反射机制的方式和通过类名.方法名的方式分别获取对象,示例如下:

1、定义一个饿汉式单例模式:

package com.rf.designPatterns.singleton.hungrySingleton;
/**
 * @description: 饿汉式单例模式
 * @author: xiaozhi
 * @create: 2020-05-23 22:26
 */
public class HungrySingleton {
    //直接声明一个需要被单例的对象,在静态块中初始化对象
    private final static HungrySingleton hungrySingleton;

    static {
        hungrySingleton=new HungrySingleton();
    }
    //私有的构造器,不被外部所访问
    private HungrySingleton(){
    }
    //提供一个外部获取HungrySingleton对象的静态方法
    public static HungrySingleton getInstance(){
        return hungrySingleton;
    }

}

2、定义一个测试类,如下图:

package com.rf.designPatterns.singleton.hungrySingleton;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
 * @description:
 * @author: xiaozhi
 * @create: 2020-05-31 20:32
 */
public class Test2 {
    public static void main(String[] args) {
        try {
            /** 
            * @Description: 通过反射获取对象
            * @Author: xz
            * @Date: 2020/5/31 21:06
            */ 
            //获取class对象
            Class hungrySingletonClass = HungrySingleton.class;
            //通过获取的class对象,获取声明的构造器
            Constructor declaredConstructor =  hungrySingletonClass.getDeclaredConstructor();
            //去除构造器的私有权限
            declaredConstructor.setAccessible(true);
            //通过构造器创建对象
            
            HungrySingleton newInstance = declaredConstructor.newInstance();
            /** 
            * @Description:  直接通过类名.getInstance方法获取对象
            * @Author: xz
            * @Date: 2020/5/31 21:07
            */ 
            HungrySingleton instance=HungrySingleton.getInstance();

            //输出2个对象,并判断是否相等
            System.out.println(newInstance);
            System.out.println(instance);
            System.out.println(newInstance==instance);

        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }  catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

3、输出结果如下:

在这里插入图片描述

1.2、饿汉式单例模式反射攻击的解决方案

1、在 饿汉式单例模式的私有构造器中加入如下代码:

 if(hungrySingleton !=null ){
       throw new RuntimeException("单例构造器进制通过反射调用。。。。");
  }

2、在运行测试类,结果如下图: 在这里插入图片描述

二、静态内部类的反射攻击及解决方法 2.1、通过反射机制的方式和通过类名.方法名的方式分别获取对象,示例如下:

1、定义一个静态内部类的单例模式,如下:

package com.rf.designPatterns.singleton.sttaicInnerClassSingleton;
/**
 * @description: 静态内部类的单例模式
 * @author: xiaozhi
 * @create: 2020-05-22 21:24
 */
public class StaticInnerClassSingleton {
    //私有的构造器,不被外部所访问
    private StaticInnerClassSingleton(){}
    //私有的静态内部类
    private static class InnerClass{
        //new一个私有的静态的StaticInnerClassSingleton对象
        private static StaticInnerClassSingleton staticInnerClassSingleton=new StaticInnerClassSingleton();
    }
    //提供一个公用的对外暴露的方法
    public static StaticInnerClassSingleton getInstance(){
        //返回值为通过静态内部内调用静态成员
        return InnerClass.staticInnerClassSingleton;
    }
}

2、定义一个测试类,如下:

package com.rf.designPatterns.singleton.sttaicInnerClassSingleton;

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

/**
 * @description:
 * @author: xiaozhi
 * @create: 2020-05-31 21:22
 */
public class Test2 {
    public static void main(String[] args) {
        try {
            /**
             * @Description: 通过反射获取对象
             * @Author: xz
             * @Date: 2020/5/31 21:06
             */
            //获取class对象
            Class staticInnerClassSingletonClass = StaticInnerClassSingleton.class;
            //通过获取的class对象,获取声明的构造器
            Constructor declaredConstructor = staticInnerClassSingletonClass.getDeclaredConstructor();
            //去除构造器的私有权限
            declaredConstructor.setAccessible(true);
            //通过构造器创建对象

            StaticInnerClassSingleton newInstance = declaredConstructor.newInstance();
            /**
             * @Description:  直接通过类名.getInstance方法获取对象
             * @Author: xz
             * @Date: 2020/5/31 21:07
             */
            StaticInnerClassSingleton instance=StaticInnerClassSingleton.getInstance();

            //输出2个对象,并判断是否相等
            System.out.println(newInstance);
            System.out.println(instance);
            System.out.println(newInstance==instance);

        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }  catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

3、输出结果如下图: 在这里插入图片描述

2.2、静态内部类的反射攻击解决方案

1、在静态内部类的私有构造器中加入如下代码:

 if(InnerClass.staticInnerClassSingleton !=null){
      throw new RuntimeException("单例构造器进制反射调用");
 }

2、在运行测试类,结果如下图: 在这里插入图片描述

三、懒汉式的反射攻击及解决方法 3.1、通过反射机制的方式和通过类名.方法名的方式分别获取对象,示例如下:

1、定义一个懒汉式的单例模式,如下:

package com.rf.designPatterns.singleton.lazyDoubleCheckSingleton;
/**
 * @description: 懒汉式双重检检查锁
 * @author: xiaozhi
 */
public class LazyDoubleCheckSingleton {
    //声明一个需要被单例的对象,初始化时没有被创建所以设置null
    /** volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。
     *  而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。
     *  这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
     */
    private volatile static LazyDoubleCheckSingleton lazyDoubleCheckSingleton=null;
    //私有的构造器,不被外部所访问
    private LazyDoubleCheckSingleton(){ }

    //提供一个外部获取LazyDoubleCheckSingleton对象的静态方法
    public  static LazyDoubleCheckSingleton getInstance(){
        if (lazyDoubleCheckSingleton==null){
            synchronized (LazyDoubleCheckSingleton.class){
                if(lazyDoubleCheckSingleton==null){
                    lazyDoubleCheckSingleton=new LazyDoubleCheckSingleton();
                }
            }
        }
        return  lazyDoubleCheckSingleton;
    }
}

2、定义一个测试类,如下:

package com.rf.designPatterns.singleton.lazySingleton;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * @description:
 * @author: xiaozhi
 * @create: 2020-05-31 21:36
 */
public class Test1 {
    public static void main(String[] args) {
        try {
            /**
             * @Description: 通过反射获取对象
             * @Author: xz
             * @Date: 2020/5/31 21:06
             */
            //获取class对象
            Class lazySingletonClass = LazySingleton.class;
            //通过获取的class对象,获取声明的构造器
            Constructor declaredConstructor =  lazySingletonClass.getDeclaredConstructor();
            //去除构造器的私有权限
            declaredConstructor.setAccessible(true);
            //通过构造器创建对象
            LazySingleton newInstance = declaredConstructor.newInstance();
            /**
             * @Description:  直接通过类名.getInstance方法获取对象
             * @Author: xz
             * @Date: 2020/5/31 21:07
             */
            LazySingleton instance=LazySingleton.getInstance();

            //输出2个对象,并判断是否相等
            System.out.println(newInstance);
            System.out.println(instance);
            System.out.println(newInstance==instance);

        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }  catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

3、输出结果如下图: 在这里插入图片描述

3.2、懒汉式无法解决反射攻击

示例1:通过反射机制没有修改私有属性的话,会解决反射攻击

1、在懒汉式的私有构造器外部声明一个boolean类型变量,构造器内部在中加入如下代码:

  
package com.rf.designPatterns.singleton.lazyDoubleCheckSingleton;
/**
 * @description: 懒汉式双重检检查锁
 * @author: xiaozhi
 */
public class LazyDoubleCheckSingleton {
    //声明一个需要被单例的对象,初始化时没有被创建所以设置null
    /** volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。
     *  而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。
     *  这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
     */
    private volatile static LazyDoubleCheckSingleton lazyDoubleCheckSingleton=null;
    private static boolean flag =true;
    //私有的构造器,不被外部所访问
    private LazyDoubleCheckSingleton(){
        if(flag){
            flag=false;
        }else{
            throw new RuntimeException("单例构造器进制反射调用");
        }
    }

    //提供一个外部获取LazyDoubleCheckSingleton对象的静态方法
    public  static LazyDoubleCheckSingleton getInstance(){
        if (lazyDoubleCheckSingleton==null){
            synchronized (LazyDoubleCheckSingleton.class){
                if(lazyDoubleCheckSingleton==null){
                    lazyDoubleCheckSingleton=new LazyDoubleCheckSingleton();
                }
            }
        }
        return  lazyDoubleCheckSingleton;
    }
}

2、测试类如下:

package com.rf.designPatterns.singleton.lazySingleton;

import com.rf.designPatterns.singleton.hungrySingleton.HungrySingleton;

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

/**
 * @description:
 * @author: xiaozhi
 * @create: 2020-05-31 21:36
 */
public class Test1 {
    public static void main(String[] args) {
        try {
            /**
             * @Description: 通过反射获取对象
             * @Author: xz
             * @Date: 2020/5/31 21:06
             */
            //获取class对象
            Class lazySingletonClass = LazySingleton.class;
            //通过获取的class对象,获取声明的构造器
            Constructor declaredConstructor =  lazySingletonClass.getDeclaredConstructor();
            //去除构造器的私有权限
            declaredConstructor.setAccessible(true);
            //通过构造器创建对象
            LazySingleton newInstance = declaredConstructor.newInstance();
            /**
             * @Description:  直接通过类名.getInstance方法获取对象
             * @Author: xz
             * @Date: 2020/5/31 21:07
             */
            LazySingleton instance=LazySingleton.getInstance();

            //输出2个对象,并判断是否相等
            System.out.println(newInstance);
            System.out.println(instance);
            System.out.println(newInstance==instance);

        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }  catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

3、在运行测试类,结果如下图: 在这里插入图片描述

示例2:通过反射机制修改私有属性的话,无法解决反射攻击

1、在懒汉式的私有构造器外部声明一个boolean类型变量,构造器内部在中加入如下代码:

package com.rf.designPatterns.singleton.lazyDoubleCheckSingleton;
/**
 * @description: 懒汉式双重检检查锁
 * @author: xiaozhi
 */
public class LazyDoubleCheckSingleton {
    //声明一个需要被单例的对象,初始化时没有被创建所以设置null
    /** volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。
     *  而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。
     *  这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
     */
    private volatile static LazyDoubleCheckSingleton lazyDoubleCheckSingleton=null;
    private static boolean flag =true;
    //私有的构造器,不被外部所访问
    private LazyDoubleCheckSingleton(){
        if(flag){
            flag=false;
        }else{
            throw new RuntimeException("单例构造器进制反射调用");
        }
    }

    //提供一个外部获取LazyDoubleCheckSingleton对象的静态方法
    public  static LazyDoubleCheckSingleton getInstance(){
        if (lazyDoubleCheckSingleton==null){
            synchronized (LazyDoubleCheckSingleton.class){
                if(lazyDoubleCheckSingleton==null){
                    lazyDoubleCheckSingleton=new LazyDoubleCheckSingleton();
                }
            }
        }
        return  lazyDoubleCheckSingleton;
    }
}

2、测试类如下:

package com.rf.designPatterns.singleton.lazySingleton;

import com.rf.designPatterns.singleton.hungrySingleton.HungrySingleton;

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

/**
 * @description:
 * @author: xiaozhi
 * @create: 2020-05-31 21:36
 */
public class Test1 {
    public static void main(String[] args) {
        try {
            /**
             * @Description: 通过反射获取对象
             * @Author: xz
             * @Date: 2020/5/31 21:06
             */
            //获取class对象
            Class lazySingletonClass = LazySingleton.class;
            //通过获取的class对象,获取声明的构造器
            Constructor declaredConstructor =  lazySingletonClass.getDeclaredConstructor();
            //去除构造器的私有权限
            declaredConstructor.setAccessible(true);

            LazySingleton instance=LazySingleton.getInstance();
            //修改懒汉式的私有属性
            Field  flag = instance.getClass().getDeclaredField("flag");
            flag.setAccessible(true);
            flag.set(instance,true);

            //通过构造器创建对象
            LazySingleton newInstance = declaredConstructor.newInstance();

            //输出2个对象,并判断是否相等
            System.out.println(newInstance);
            System.out.println(instance);
            System.out.println(newInstance==instance);

        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }  catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }
}

3、输出结果如下: 在这里插入图片描述

四、结论
  • 单例模式中的饿汉式和静态内部类:由于类初始化时,没有创建对象,可以在构造器中判断对象是否为null,防止反射攻击
  • 单例模式中的懒汉式:由于类初始化时就会创建对象,所以在可以在构造器中如何加复杂的逻辑都不会防止反射攻击
关注
打赏
1661269038
查看更多评论
立即登录/注册

微信扫码登录

0.0472s