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)); + } +}