非类加载懒汉式单例模式反射攻击和防御测试
This commit is contained in:
@@ -16,6 +16,7 @@ public class LazySingletonSynchronized {
|
||||
* 单例模式构造器必须是屏蔽的
|
||||
*/
|
||||
private LazySingletonSynchronized() {
|
||||
log.debug(LazySingletonSynchronized.class.getSimpleName() + "构造器实例化");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
// 打开访问权限
|
||||
|
||||
Reference in New Issue
Block a user