From 02e0e79bdfb39f13f21845ed0742e5b4a7365ff9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=E5=98=89=E9=98=B3?= Date: Tue, 25 Sep 2018 22:52:37 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8D=95=E4=BE=8B=E6=A8=A1=E5=BC=8F=E5=85=8B?= =?UTF-8?q?=E9=9A=86=E6=94=BB=E5=87=BB=E4=BB=A5=E5=8F=8A=E9=98=B2=E5=BE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../HungrySingletonCloneableImprove.java | 52 ++++++++++++++++++ .../HungrySingletonReflectImprove.java | 7 ++- .../singleton/SingletonCloneAttack.java | 54 +++++++++++++++++++ 3 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 src/main/java/top/fjy8018/designpattern/pattern/creational/singleton/HungrySingletonCloneableImprove.java create mode 100644 src/test/java/top/fjy8018/designpattern/pattern/creational/singleton/SingletonCloneAttack.java diff --git a/src/main/java/top/fjy8018/designpattern/pattern/creational/singleton/HungrySingletonCloneableImprove.java b/src/main/java/top/fjy8018/designpattern/pattern/creational/singleton/HungrySingletonCloneableImprove.java new file mode 100644 index 0000000..2f53d4e --- /dev/null +++ b/src/main/java/top/fjy8018/designpattern/pattern/creational/singleton/HungrySingletonCloneableImprove.java @@ -0,0 +1,52 @@ +package top.fjy8018.designpattern.pattern.creational.singleton; + +import lombok.extern.slf4j.Slf4j; + +import java.io.Serializable; + +/** + * 单例模式:饿汉式 + * 克隆攻击改进 + * 要么不要实现克隆接口,要么不要调用父类的克隆实现,转而自己实现克隆方法 + * + * @author F嘉阳 + * @date 2018-09-24 15:38 + */ +@Slf4j +public class HungrySingletonCloneableImprove implements Serializable, Cloneable { + + private static final HungrySingletonCloneableImprove HUNGRYSINGLETON; + + static { + log.debug(HungrySingletonCloneableImprove.class.getSimpleName() + "静态块实例化"); + HUNGRYSINGLETON = new HungrySingletonCloneableImprove(); + } + + private HungrySingletonCloneableImprove() { + if (HUNGRYSINGLETON != null) { + throw new RuntimeException("单例模式禁止反射调用"); + } + log.debug(HungrySingletonCloneableImprove.class.getSimpleName() + "构造器实例化"); + } + + public static HungrySingletonCloneableImprove getInstance() { + return HUNGRYSINGLETON; + } + + @Override + protected Object clone() throws CloneNotSupportedException { + return HUNGRYSINGLETON; + } + + /** + * 解决序列化攻击问题 + * + * @return + */ + private Object readResolve() { + log.debug("序列化获取对象"); + return HUNGRYSINGLETON; + } + + +} diff --git a/src/main/java/top/fjy8018/designpattern/pattern/creational/singleton/HungrySingletonReflectImprove.java b/src/main/java/top/fjy8018/designpattern/pattern/creational/singleton/HungrySingletonReflectImprove.java index f72d934..159d551 100644 --- a/src/main/java/top/fjy8018/designpattern/pattern/creational/singleton/HungrySingletonReflectImprove.java +++ b/src/main/java/top/fjy8018/designpattern/pattern/creational/singleton/HungrySingletonReflectImprove.java @@ -13,7 +13,7 @@ import java.io.Serializable; * @date 2018-09-24 15:38 */ @Slf4j -public class HungrySingletonReflectImprove implements Serializable { +public class HungrySingletonReflectImprove implements Serializable, Cloneable { private static final HungrySingletonReflectImprove HUNGRYSINGLETON; @@ -29,6 +29,11 @@ public class HungrySingletonReflectImprove implements Serializable { log.debug(HungrySingletonReflectImprove.class.getSimpleName() + "构造器实例化"); } + @Override + protected Object clone() throws CloneNotSupportedException { + return super.clone(); + } + public static HungrySingletonReflectImprove getInstance() { return HUNGRYSINGLETON; } diff --git a/src/test/java/top/fjy8018/designpattern/pattern/creational/singleton/SingletonCloneAttack.java b/src/test/java/top/fjy8018/designpattern/pattern/creational/singleton/SingletonCloneAttack.java new file mode 100644 index 0000000..cb1eea2 --- /dev/null +++ b/src/test/java/top/fjy8018/designpattern/pattern/creational/singleton/SingletonCloneAttack.java @@ -0,0 +1,54 @@ +package top.fjy8018.designpattern.pattern.creational.singleton; + +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Method; + +/** + * 单例模式克隆攻击 + * + * @author F嘉阳 + * @date 2018-09-25 22:40 + */ +@Slf4j +class SingletonCloneAttack { + + /** + * 改进前 + */ + @Test + void before() throws Exception { + // 正常方式获取单例 + HungrySingletonReflectImprove instance = HungrySingletonReflectImprove.getInstance(); + Method method = instance.getClass().getDeclaredMethod("clone"); + // 由于原本的访问权限为protect,故要改变访问权限 + method.setAccessible(true); + // 克隆实例 + HungrySingletonReflectImprove instance2 = (HungrySingletonReflectImprove) method.invoke(instance); + // 验证 + log.info(Integer.toHexString(instance.hashCode())); + log.info(Integer.toHexString(instance2.hashCode())); + // 返回false,克隆攻击成功 + log.info(String.valueOf(instance == instance2)); + } + + /** + * 改进后 + */ + @Test + void after() throws Exception { + // 正常方式获取单例 + HungrySingletonCloneableImprove instance = HungrySingletonCloneableImprove.getInstance(); + Method method = instance.getClass().getDeclaredMethod("clone"); + // 由于原本的访问权限为protect,故要改变访问权限 + method.setAccessible(true); + // 克隆实例 + HungrySingletonCloneableImprove instance2 = (HungrySingletonCloneableImprove) method.invoke(instance); + // 验证 + log.info(Integer.toHexString(instance.hashCode())); + log.info(Integer.toHexString(instance2.hashCode())); + // 返回false,克隆攻击成功 + log.info(String.valueOf(instance == instance2)); + } +}