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