如何防止单例模式被JAVA反射攻击
欢迎支持笔者新作:《深入理解Kafka:核心设计与实践原理》和《RabbitMQ实战指南》,同时欢迎关注笔者的微信公众号:朱小厮的博客。
欢迎跳转到本文的原文链接:https://honeypps.com/java/how-to-prevent-singleton-from-reflect/
单例模式相信大家都知道,用过的人不在少数。之前写过一篇博文《singleton模式四种线程安全的实现》(参见:http://blog.csdn.net/u013256816/article/details/50427061),讲诉了单例模式的四种写法,并指出占位符模式的写法比较ok,详见如下:
package com.effective.singleton;public class Elvis
{private static boolean flag = false;private Elvis(){}private static class SingletonHolder{private static final Elvis INSTANCE = new Elvis();}public static Elvis getInstance(){return SingletonHolder.INSTANCE;}public void doSomethingElse(){}
}
但这都是基于一个条件:确保不会通过反射机制调用私有的构造器。
这里举个例子,通过JAVA的反射机制来“攻击”单例模式:
package com.effective.singleton;import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;public class ElvisReflectAttack
{public static void main(String[] args) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException{Class<?> classType = Elvis.class;Constructor<?> c = classType.getDeclaredConstructor(null);c.setAccessible(true);Elvis e1 = (Elvis)c.newInstance();Elvis e2 = Elvis.getInstance();System.out.println(e1==e2);}}
运行结果:false
可以看到,通过反射获取构造函数,然后调用setAccessible(true)就可以调用私有的构造函数,所有e1和e2是两个不同的对象。
如果要抵御这种攻击,可以修改构造器,让它在被要求创建第二个实例的时候抛出异常。
经修改后:
package com.effective.singleton;public class ElvisModified
{private static boolean flag = false;private ElvisModified(){synchronized(ElvisModified.class){if(flag == false){flag = !flag;}else{throw new RuntimeException("单例模式被侵犯!");}}}private static class SingletonHolder{private static final ElvisModified INSTANCE = new ElvisModified();}public static ElvisModified getInstance(){return SingletonHolder.INSTANCE;}public void doSomethingElse(){}
}
测试代码:
package com.effective.singleton;import java.lang.reflect.Constructor;public class ElvisModifiedReflectAttack
{public static void main(String[] args){try{Class<ElvisModified> classType = ElvisModified.class;Constructor<ElvisModified> c = classType.getDeclaredConstructor(null);c.setAccessible(true);ElvisModified e1 = (ElvisModified)c.newInstance();ElvisModified e2 = ElvisModified.getInstance();System.out.println(e1==e2);}catch (Exception e){e.printStackTrace();}}
}
运行结果:
Exception in thread "main" java.lang.ExceptionInInitializerErrorat com.effective.singleton.ElvisModified.getInstance(ElvisModified.java:27)at com.effective.singleton.ElvisModifiedReflectAttack.main(ElvisModifiedReflectAttack.java:17)
Caused by: java.lang.RuntimeException: 单例模式被侵犯!at com.effective.singleton.ElvisModified.<init>(ElvisModified.java:16)at com.effective.singleton.ElvisModified.<init>(ElvisModified.java:7)at com.effective.singleton.ElvisModified$SingletonHolder.<clinit>(ElvisModified.java:22)... 2 more
可以看到,成功的阻止了单例模式被破坏。
从JDK1.5开始,实现Singleton还有新的写法,只需编写一个包含单个元素的枚举类型。推荐写法:
package com.effective.singleton;public enum SingletonClass
{INSTANCE;public void test(){System.out.println("The Test!");}
}
测试代码:
package com.effective;import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;import com.effective.singleton.SingletonClass;public class TestMain
{public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{Class<SingletonClass> classType = SingletonClass.class;Constructor<SingletonClass> c = (Constructor<SingletonClass>) classType.getDeclaredConstructor();c.setAccessible(true);c.newInstance();}
}
运行结果:
Exception in thread "main" java.lang.NoSuchMethodException: com.effective.singleton.SingletonClass.<init>()at java.lang.Class.getConstructor0(Unknown Source)at java.lang.Class.getDeclaredConstructor(Unknown Source)at com.effective.TestMain.main(TestMain.java:22)
由此可见这种写法也可以防止单例模式被“攻击”。
而且这种写法也可以防止序列化破坏单例模式,具体不在举例了,有关序列化以及单例模式被序列化破坏可以参考博文《JAVA序列化》(链接:http://blog.csdn.net/u013256816/article/details/50474678)。
单元素的枚举类型已经成为实现Singleton模式的最佳方法。
欢迎跳转到本文的原文链接:https://honeypps.com/java/how-to-prevent-singleton-from-reflect/
欢迎支持笔者新作:《深入理解Kafka:核心设计与实践原理》和《RabbitMQ实战指南》,同时欢迎关注笔者的微信公众号:朱小厮的博客。
如何防止单例模式被JAVA反射攻击相关推荐
- java 防止反射_如何防止JAVA反射对单例类的攻击?
在我的上篇随笔中,我们知道了创建单例类有以下几种方式: (1).饿汉式; (2).懒汉式(.加同步锁的懒汉式.加双重校验锁的懒汉式.防止指令重排优化的懒汉式); (3).登记式单例模式; (4).静态 ...
- Java设计模式——为什么要用枚举实现单例模式(避免反射、序列化问题)
1.序言 相信如果能看到我这篇博客的小伙伴,肯定都看过Joshua Bloch大神说过的这句话:"单元素的枚举类型已经成为实现Singleton的最佳方法".其实,第一次读到这 ...
- java 防止反射_Java设计模式(一):单例模式,防止反射和反序列化漏洞
一.懒汉式单例模式,解决反射和反序列化漏洞 package com.iter.devbox.singleton; import java.io.ObjectStreamException; impor ...
- java 反射 单列_轻松掌握Java单例模式
单例模式是23中设计模式中最简单的设计模式,在企业开发中也应用的特别多.单例模式的优点是:项目中有且仅有一个实例. 特点:构造器私有化,对象私有化,只提供一个对外访问的接口. 应用场景: 1.系统需要 ...
- 单例设计模式-反射攻击解决方案及原理分析
package com.learn.design.pattern.creational.singleton;import java.io.Serializable;public class Hungr ...
- 为什么要用枚举实现单例模式(避免反射、序列化问题)
1 引言 相信如果能看到我这篇博客的小伙伴,肯定都看过Joshua Bloch大神说过的这句话:"单元素的枚举类型已经成为实现Singleton的最佳方法".其实,第一次读到这 ...
- Java 反射?反射有什么缺点?你是怎么理解反射的(为什么框架需要反射)?
ava Java 基础这部分一些简单的问题就直接没有放上来. 需要资料的朋友麻烦一键三连之后点此免费获取! 基础: Java 反射?反射有什么缺点?你是怎么理解反射的(为什么框架需要反射)? 谈谈对 ...
- Java反射设计模式
Java反射&设计模式 反射机制 什么是Java反射 反射机制的作用 反射机制的应用场景 反射机制获取类的三种方法 反射创建对象的方式 反射创建api 使用反射为类私有属性赋值 JDBC反射加 ...
- java 反射详解通俗易懂
Java的反射机制是Java特性之一,反射机制是构建框架技术的基础所在.灵活掌握Java反射机制,对大家以后学习框架技术有很大的帮助. 那么什么是Java的反射呢? 大家都知道,要让Java程序能够运 ...
最新文章
- (组合)Binomial Showdown
- 小学生学python到底能干什么-小学生都学Python了,你还没用万矿?
- LeetCode-剑指 Offer 50. 第一个只出现一次的字符
- Python中有几种办法交换两个变量的值?
- request 和require区别_JAVA WEB开发中涉及到的get和post请求,他们的区别
- 获取listview当前滚动的高度
- 机器学习笔记(《统计学习方法》李航and《机器学习》周志华)
- Linux配置本地/外网访问Apache服务器,手把手教你搭建Ngrok——以小米球Ngrok为例
- 服务器c盘logs文件夹,c盘的logs文件夹有什么用
- android使用Itext库生成PDF文件
- 计算机绘图说课视频,机械图识读与计算机绘图说课PPT课件.ppt
- 如何禁用电脑文件共享
- 读书寄语:有一种感动叫守口如瓶
- linux quota原理,[转载]linux下quota实现
- 家装灯线走线图_家装吊顶筒灯电线走法设计图 4款客厅天花吊顶龙骨筒灯顶线布置图...
- jQuery中的动画 -- 案例
- Illegal unquoted character ((CTRL-CHAR, code 10)): has to be escaped using backslash to be included
- exit() _exit()
- OpenAI 首个研究成果 生成式模型系列
- 苹果公司的企业文化_【全景标杆】乔布斯给苹果留下了什么样的企业文化?