From b227587e3f7467f3d19f74a99b73be6b17de7b33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=E5=98=89=E9=98=B3?= Date: Mon, 24 Sep 2018 17:28:29 +0800 Subject: [PATCH] =?UTF-8?q?=E9=9D=9E=E7=B1=BB=E5=8A=A0=E8=BD=BD=E6=87=92?= =?UTF-8?q?=E6=B1=89=E5=BC=8F=E5=8D=95=E4=BE=8B=E6=A8=A1=E5=BC=8F=E5=8F=8D?= =?UTF-8?q?=E5=B0=84=E6=94=BB=E5=87=BB=E5=92=8C=E9=98=B2=E5=BE=A1=E6=B5=8B?= =?UTF-8?q?=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../singleton/LazySingletonSynchronized.java | 1 + ...ySingletonSynchronizedReflectImprove1.java | 59 +++++++++++ ...ySingletonSynchronizedReflectImprove2.java | 63 ++++++++++++ .../singleton/SingletonReflectAttack.java | 98 ++++++++++++++++++- 4 files changed, 217 insertions(+), 4 deletions(-) create mode 100644 src/main/java/top/fjy8018/designpattern/pattern/creational/singleton/LazySingletonSynchronizedReflectImprove1.java create mode 100644 src/main/java/top/fjy8018/designpattern/pattern/creational/singleton/LazySingletonSynchronizedReflectImprove2.java diff --git a/src/main/java/top/fjy8018/designpattern/pattern/creational/singleton/LazySingletonSynchronized.java b/src/main/java/top/fjy8018/designpattern/pattern/creational/singleton/LazySingletonSynchronized.java index 7b07f5a..2a23f2b 100644 --- a/src/main/java/top/fjy8018/designpattern/pattern/creational/singleton/LazySingletonSynchronized.java +++ b/src/main/java/top/fjy8018/designpattern/pattern/creational/singleton/LazySingletonSynchronized.java @@ -16,6 +16,7 @@ public class LazySingletonSynchronized { * 单例模式构造器必须是屏蔽的 */ private LazySingletonSynchronized() { + log.debug(LazySingletonSynchronized.class.getSimpleName() + "构造器实例化"); } /** diff --git a/src/main/java/top/fjy8018/designpattern/pattern/creational/singleton/LazySingletonSynchronizedReflectImprove1.java b/src/main/java/top/fjy8018/designpattern/pattern/creational/singleton/LazySingletonSynchronizedReflectImprove1.java new file mode 100644 index 0000000..dbe5e75 --- /dev/null +++ b/src/main/java/top/fjy8018/designpattern/pattern/creational/singleton/LazySingletonSynchronizedReflectImprove1.java @@ -0,0 +1,59 @@ +package top.fjy8018.designpattern.pattern.creational.singleton; + +import lombok.extern.slf4j.Slf4j; + +/** + * 懒汉式单例:延迟加载(线程安全) + * + * @author F嘉阳 + * @date 2018-09-24 10:29 + */ +@Slf4j +public class LazySingletonSynchronizedReflectImprove1 { + private static LazySingletonSynchronizedReflectImprove1 lazySingleton = null; + + /** + * 单例模式构造器必须是屏蔽的 + * 对于非类加载时实例化的单例该方法防止反射攻击无效 + * 因为当反射先执行,则会实例化一个或者N个对象 + * 当正常方式获取单例时,又会实例化多一个对象 + */ + private LazySingletonSynchronizedReflectImprove1() { + if (lazySingleton != null) { + throw new RuntimeException("单例模式禁止反射调用"); + } + log.debug(LazySingletonSynchronizedReflectImprove1.class.getSimpleName() + "构造器实例化"); + } + + /** + * synchronized确保线程安全 + * synchronized用于静态方法是对class文件加锁,用于普通方法则是对堆内存中的实例方法加锁 + * synchronized(LazySingletonSynchronized.class)与在方法名上加锁效果一致 + * + * @return + */ + public static LazySingletonSynchronizedReflectImprove1 getInstance() { + synchronized (LazySingletonSynchronizedReflectImprove1.class) { + if (lazySingleton == null) { + log.debug(LazySingletonSynchronizedReflectImprove1.class.getSimpleName() + "实例化"); + lazySingleton = new LazySingletonSynchronizedReflectImprove1(); + } + } + return lazySingleton; + } + + /** + * 线程安全验证 + * + * @return + */ + public synchronized static LazySingletonSynchronizedReflectImprove1 getInstance2() throws InterruptedException { + if (lazySingleton == null) { + // 使用线程等待模拟复杂实例化过程,让多线程同时进入该方法 + Thread.sleep(1000); + log.debug(LazySingletonSynchronizedReflectImprove1.class.getSimpleName() + "实例化"); + lazySingleton = new LazySingletonSynchronizedReflectImprove1(); + } + return lazySingleton; + } +} diff --git a/src/main/java/top/fjy8018/designpattern/pattern/creational/singleton/LazySingletonSynchronizedReflectImprove2.java b/src/main/java/top/fjy8018/designpattern/pattern/creational/singleton/LazySingletonSynchronizedReflectImprove2.java new file mode 100644 index 0000000..ed27b01 --- /dev/null +++ b/src/main/java/top/fjy8018/designpattern/pattern/creational/singleton/LazySingletonSynchronizedReflectImprove2.java @@ -0,0 +1,63 @@ +package top.fjy8018.designpattern.pattern.creational.singleton; + +import lombok.extern.slf4j.Slf4j; + +/** + * 懒汉式单例:延迟加载(线程安全) + * + * @author F嘉阳 + * @date 2018-09-24 10:29 + */ +@Slf4j +public class LazySingletonSynchronizedReflectImprove2 { + private static LazySingletonSynchronizedReflectImprove2 lazySingleton = null; + + /** + * 是否能实例化标志 + * 若能实例化,则为{@code true} + */ + private static boolean flag = true; + + /** + * 单例模式构造器必须是屏蔽的 + */ + private LazySingletonSynchronizedReflectImprove2() { + if (!flag) { + throw new RuntimeException("单例模式禁止反射调用"); + } + flag = false; + log.debug(LazySingletonSynchronizedReflectImprove2.class.getSimpleName() + "构造器实例化"); + } + + /** + * synchronized确保线程安全 + * synchronized用于静态方法是对class文件加锁,用于普通方法则是对堆内存中的实例方法加锁 + * synchronized(LazySingletonSynchronized.class)与在方法名上加锁效果一致 + * + * @return + */ + public static LazySingletonSynchronizedReflectImprove2 getInstance() { + synchronized (LazySingletonSynchronizedReflectImprove2.class) { + if (lazySingleton == null) { + log.debug(LazySingletonSynchronizedReflectImprove2.class.getSimpleName() + "实例化"); + lazySingleton = new LazySingletonSynchronizedReflectImprove2(); + } + } + return lazySingleton; + } + + /** + * 线程安全验证 + * + * @return + */ + public synchronized static LazySingletonSynchronizedReflectImprove2 getInstance2() throws InterruptedException { + if (lazySingleton == null) { + // 使用线程等待模拟复杂实例化过程,让多线程同时进入该方法 + Thread.sleep(1000); + log.debug(LazySingletonSynchronizedReflectImprove2.class.getSimpleName() + "实例化"); + lazySingleton = new LazySingletonSynchronizedReflectImprove2(); + } + return lazySingleton; + } +} diff --git a/src/test/java/top/fjy8018/designpattern/pattern/creational/singleton/SingletonReflectAttack.java b/src/test/java/top/fjy8018/designpattern/pattern/creational/singleton/SingletonReflectAttack.java index b7d8a99..717314a 100644 --- a/src/test/java/top/fjy8018/designpattern/pattern/creational/singleton/SingletonReflectAttack.java +++ b/src/test/java/top/fjy8018/designpattern/pattern/creational/singleton/SingletonReflectAttack.java @@ -4,6 +4,7 @@ import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Test; import java.lang.reflect.Constructor; +import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import static org.junit.jupiter.api.Assertions.*; @@ -14,6 +15,95 @@ import static org.junit.jupiter.api.Assertions.*; @Slf4j class SingletonReflectAttack { + /** + * 修复前懒汉式反射攻击 + * 由于反射攻击足够强大,对于非类加载时实例化的单例模式,是无法彻底防止反射攻击的 + * + * @throws NoSuchMethodException + * @throws IllegalAccessException + * @throws InvocationTargetException + * @throws InstantiationException + */ + @Test + void lazySingletonGetInstanceBefore() throws Exception { + Class clazz = LazySingletonSynchronized.class; + Constructor constructor = clazz.getDeclaredConstructor(); + // 打开访问权限 + constructor.setAccessible(true); + // 通过反射获取实例 + LazySingletonSynchronized reflectInstance = (LazySingletonSynchronized) constructor.newInstance(); + // 通过正常方式获取 + LazySingletonSynchronized instance = LazySingletonSynchronized.getInstance(); + + // 判断是否同一个对象 + log.info("反射方式:" + reflectInstance); + log.info("正常方式:" + instance); + // 此处结果为false,说明是两个不同的对象 + log.info(String.valueOf(reflectInstance == instance)); + } + + /** + * 用构造器防止单例模式被反射攻击 + * 失败! + * + * @throws NoSuchMethodException + * @throws IllegalAccessException + * @throws InvocationTargetException + * @throws InstantiationException + */ + @Test + void lazySingletonGetInstanceAfter1() throws Exception { + Class clazz = LazySingletonSynchronizedReflectImprove1.class; + Constructor constructor = clazz.getDeclaredConstructor(); + // 打开访问权限 + constructor.setAccessible(true); + // 通过反射获取实例 + LazySingletonSynchronizedReflectImprove1 reflectInstance = (LazySingletonSynchronizedReflectImprove1) constructor.newInstance(); + // 通过正常方式获取 + LazySingletonSynchronizedReflectImprove1 instance = LazySingletonSynchronizedReflectImprove1.getInstance(); + + // 判断是否同一个对象 + log.info("反射方式:" + reflectInstance); + log.info("正常方式:" + instance); + // 此处结果为false,说明是两个不同的对象 + log.info(String.valueOf(reflectInstance == instance)); + } + + /** + * 用标识位防止单例模式被反射攻击 + * 失败! + * + * @throws NoSuchMethodException + * @throws IllegalAccessException + * @throws InvocationTargetException + * @throws InstantiationException + */ + @Test + void lazySingletonGetInstanceAfter2() throws Exception { + Class clazz = LazySingletonSynchronizedReflectImprove2.class; + Constructor constructor = clazz.getDeclaredConstructor(); + // 打开访问权限 + constructor.setAccessible(true); + + // 首先通过正常方式获取 + LazySingletonSynchronizedReflectImprove2 instance = LazySingletonSynchronizedReflectImprove2.getInstance(); + + // 通过反射修改成员变量 + Field flag = clazz.getDeclaredField("flag"); + // 修改访问权限 + flag.setAccessible(true); + flag.set(clazz, true); + + // 通过反射获取实例 + LazySingletonSynchronizedReflectImprove2 reflectInstance = (LazySingletonSynchronizedReflectImprove2) constructor.newInstance(); + + // 判断是否同一个对象 + log.info("反射方式:" + reflectInstance); + log.info("正常方式:" + instance); + // 此处结果为false,说明是两个不同的对象 + log.info(String.valueOf(reflectInstance == instance)); + } + /** * 修复前饿汉式攻击 * @@ -23,7 +113,7 @@ class SingletonReflectAttack { * @throws InstantiationException */ @Test - void HungrySingletongetInstanceBefore() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { + void hungrySingletonGetInstanceBefore() throws Exception { Class clazz = HungrySingletonSerializableImprove.class; Constructor constructor = clazz.getDeclaredConstructor(); // 打开访问权限 @@ -49,7 +139,7 @@ class SingletonReflectAttack { * @throws InstantiationException */ @Test - void HungrySingletongetInstanceAfter() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { + void hungrySingletonGetInstanceAfter() throws Exception { Class clazz = HungrySingletonReflectImprove.class; Constructor constructor = clazz.getDeclaredConstructor(); // 打开访问权限 @@ -74,7 +164,7 @@ class SingletonReflectAttack { * @throws InstantiationException */ @Test - void StaticInnerClassSingletongetInstanceBefore() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { + void staticInnerClassSingletonGetInstanceBefore() throws Exception { Class clazz = StaticInnerClassSingleton.class; Constructor constructor = clazz.getDeclaredConstructor(); // 打开访问权限 @@ -100,7 +190,7 @@ class SingletonReflectAttack { * @throws InstantiationException */ @Test - void StaticInnerClassSingletongetInstanceAfter() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { + void staticInnerClassSingletonGetInstanceAfter() throws Exception { Class clazz = StaticInnerClassSingletonReflectImprove.class; Constructor constructor = clazz.getDeclaredConstructor(); // 打开访问权限