非类加载懒汉式单例模式反射攻击和防御测试

This commit is contained in:
2018-09-24 17:28:29 +08:00
parent 5251def1b0
commit b227587e3f
4 changed files with 217 additions and 4 deletions

View File

@@ -16,6 +16,7 @@ public class LazySingletonSynchronized {
* 单例模式构造器必须是屏蔽的 * 单例模式构造器必须是屏蔽的
*/ */
private LazySingletonSynchronized() { private LazySingletonSynchronized() {
log.debug(LazySingletonSynchronized.class.getSimpleName() + "构造器实例化");
} }
/** /**

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -4,6 +4,7 @@ import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
@@ -14,6 +15,95 @@ import static org.junit.jupiter.api.Assertions.*;
@Slf4j @Slf4j
class SingletonReflectAttack { 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 * @throws InstantiationException
*/ */
@Test @Test
void HungrySingletongetInstanceBefore() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { void hungrySingletonGetInstanceBefore() throws Exception {
Class clazz = HungrySingletonSerializableImprove.class; Class clazz = HungrySingletonSerializableImprove.class;
Constructor constructor = clazz.getDeclaredConstructor(); Constructor constructor = clazz.getDeclaredConstructor();
// 打开访问权限 // 打开访问权限
@@ -49,7 +139,7 @@ class SingletonReflectAttack {
* @throws InstantiationException * @throws InstantiationException
*/ */
@Test @Test
void HungrySingletongetInstanceAfter() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { void hungrySingletonGetInstanceAfter() throws Exception {
Class clazz = HungrySingletonReflectImprove.class; Class clazz = HungrySingletonReflectImprove.class;
Constructor constructor = clazz.getDeclaredConstructor(); Constructor constructor = clazz.getDeclaredConstructor();
// 打开访问权限 // 打开访问权限
@@ -74,7 +164,7 @@ class SingletonReflectAttack {
* @throws InstantiationException * @throws InstantiationException
*/ */
@Test @Test
void StaticInnerClassSingletongetInstanceBefore() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { void staticInnerClassSingletonGetInstanceBefore() throws Exception {
Class clazz = StaticInnerClassSingleton.class; Class clazz = StaticInnerClassSingleton.class;
Constructor constructor = clazz.getDeclaredConstructor(); Constructor constructor = clazz.getDeclaredConstructor();
// 打开访问权限 // 打开访问权限
@@ -100,7 +190,7 @@ class SingletonReflectAttack {
* @throws InstantiationException * @throws InstantiationException
*/ */
@Test @Test
void StaticInnerClassSingletongetInstanceAfter() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { void staticInnerClassSingletonGetInstanceAfter() throws Exception {
Class clazz = StaticInnerClassSingletonReflectImprove.class; Class clazz = StaticInnerClassSingletonReflectImprove.class;
Constructor constructor = clazz.getDeclaredConstructor(); Constructor constructor = clazz.getDeclaredConstructor();
// 打开访问权限 // 打开访问权限