目录
一、饿汉式的反射攻击及解决方法
1.1、通过反射机制的方式和通过类名.方法名的方式分别获取对象,示例如下:
- 一、饿汉式的反射攻击及解决方法
- 1.1、通过反射机制的方式和通过类名.方法名的方式分别获取对象,示例如下:
- 1.2、饿汉式单例模式反射攻击的解决方案
- 二、静态内部类的反射攻击及解决方法
- 2.1、通过反射机制的方式和通过类名.方法名的方式分别获取对象,示例如下:
- 2.2、静态内部类的反射攻击解决方案
- 三、懒汉式的反射攻击及解决方法
- 3.1、通过反射机制的方式和通过类名.方法名的方式分别获取对象,示例如下:
- 3.2、懒汉式无法解决反射攻击
- 四、结论
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、在 饿汉式单例模式的私有构造器中加入如下代码:
if(hungrySingleton !=null ){
throw new RuntimeException("单例构造器进制通过反射调用。。。。");
}
2、在运行测试类,结果如下图:
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、输出结果如下图:
1、在静态内部类的私有构造器中加入如下代码:
if(InnerClass.staticInnerClassSingleton !=null){
throw new RuntimeException("单例构造器进制反射调用");
}
2、在运行测试类,结果如下图:
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、输出结果如下图:
示例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,防止反射攻击
- 单例模式中的懒汉式:由于类初始化时就会创建对象,所以在可以在构造器中如何加复杂的逻辑都不会防止反射攻击