单例模式:基于反射和反序列化破解单例模式的漏洞及其解决方法
单例模式使得在创建类对象的时候只创建一个对象实例。上一节讲解了五种实现单例模式的方式。
分别为:饿汉模式、懒汉模式、double check、静态内部类、枚举
但是基于反射和反序列化可以破解单例模式的单一实例,在使用反射时可以通过调用setAccesible()直接调用私有构造器,创建新的实例;在反序列化的时候会直接创建新的对象实例。但是以上漏洞只针对前四种方式,枚举由于是基于JVM底层实现机制,是天然的单例模式。
假设我们使用饿汉的实现方式创建了一个单例类:
package com.test.test1.danlimoshi;public class EHanShi {private static EHanShi eHanShi = new EHanShi();private EHanShi(){}public static EHanShi getInstance(){return eHanShi;}
}
接下来基于反射实现创建两个不同的实例。
package com.test.test1.danlimoshi;import java.lang.reflect.Constructor;public class Client2 {public static void main(String[] args) throws Exception {EHanShi eHanShi1 = EHanShi.getInstance();EHanShi eHanShi2 = EHanShi.getInstance();System.out.println(eHanShi1);System.out.println(eHanShi2);Class<EHanShi> clazz = (Class<EHanShi>) Class.forName("com.test.test1.danlimoshi.EHanShi");Constructor<EHanShi> constructor = clazz.getDeclaredConstructor(null);constructor.setAccessible(true); //跳过检查机制,直接调用私有构造器EHanShi eHanShi3 = constructor.newInstance();System.out.println(eHanShi3);}
}
打印结果:显然创新了新的实例。
com.test.test1.danlimoshi.EHanShi@1b6d3586
com.test.test1.danlimoshi.EHanShi@1b6d3586
com.test.test1.danlimoshi.EHanShi@4554617c
如何解决?
在私有构造器通过抛出异常处理。即当创建第二个实例的时候就刨出异常。
package com.test.test1.danlimoshi;public class EHanShi {private static EHanShi eHanShi = new EHanShi();private EHanShi(){if(eHanShi != null){throw new RuntimeException();}}public static EHanShi getInstance(){return eHanShi;}
}
接下来基于序列化创建新的实例。
package com.test.test1.danlimoshi;import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;public class Client2 {public static void main(String[] args) throws Exception {EHanShi eHanShi1 = EHanShi.getInstance();EHanShi eHanShi2 = EHanShi.getInstance();System.out.println(eHanShi1);System.out.println(eHanShi2);// Class<EHanShi> clazz = (Class<EHanShi>) Class.forName("com.test.test1.danlimoshi.EHanShi");
// Constructor<EHanShi> constructor = clazz.getDeclaredConstructor(null);
// constructor.setAccessible(true);
// EHanShi eHanShi3 = constructor.newInstance();
// System.out.println(eHanShi3);try(FileOutputStream fos = new FileOutputStream("D:/a.txt")){ObjectOutputStream oos = new ObjectOutputStream(fos);oos.writeObject(eHanShi1);}catch (Exception e){e.printStackTrace();}FileInputStream fis = new FileInputStream("D:/a.txt");ObjectInputStream ois = new ObjectInputStream(fis);EHanShi eHanShi3 = (EHanShi)ois.readObject();System.out.println(eHanShi3);}
}
打印结果:显然创建了新的实例。
com.test.test1.danlimoshi.EHanShi@1b6d3586
com.test.test1.danlimoshi.EHanShi@1b6d3586
com.test.test1.danlimoshi.EHanShi@6d03e736
如何解决?
在单例类中创建一个方法readResolve(),基于回调机制,在反序列化的时候直接会调用这个方法。返回当前实例。
package com.test.test1.danlimoshi;import java.io.Serializable;public class EHanShi implements Serializable {private static EHanShi eHanShi = new EHanShi();private EHanShi(){if(eHanShi != null){throw new RuntimeException();}}public static EHanShi getInstance(){return eHanShi;}public Object readResolve(){return eHanShi;}
}
那么就不会创建新的实例了。
测试五种实现方式的耗时:
package com.test.test1.danlimoshi;import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit;//测试多线程环境下实现这几种方式的耗时 public class Client3 {public static void main(String[] args) throws Exception {long startTime = System.currentTimeMillis();int maxCount = 10;CountDownLatch countDownLatch = new CountDownLatch(maxCount);for(int i =0;i<maxCount;i++) {new Thread(new Runnable() {@Overridepublic void run() {for (int j = 0; j < 100000; j++) {//Object o = EHanShi.getInstance();//Object o =LanHanShi.getInstance();Object o =JingTaiLeiJiaZai.getInstance();}countDownLatch.countDown();}}).start();}countDownLatch.await();//当10个线程执行完成,也即是计数器值为0,main线程继续往下执行long endTime = System.currentTimeMillis();System.out.println("总耗时:"+(endTime-startTime));}}
单例模式:基于反射和反序列化破解单例模式的漏洞及其解决方法相关推荐
- python怎么安装pyecharts_基于Python安装pyecharts所遇的问题及解决方法
最近学习到数据可视化内容,老师推荐安装pyecharts,于是pip install 了一下,结果...掉坑了,下面是我的跳坑经验,如果你有类似问题,希望对你有所帮助. 第一个坑: 这个不难理解,缺少 ...
- python安装pyecharts清华_基于Python安装pyecharts所遇的问题及解决方法
最近学习到数据可视化内容,老师推荐安装pyecharts,于是pip install 了一下,结果...掉坑了,下面是我的跳坑经验,如果你有类似问题,希望对你有所帮助. 第一个坑: 这个不难理解,缺少 ...
- 010Editor破解版png模板失效解决方法
这两天用010Editor, 莫名发现png模板加载错误. 提示 ''ERROR Line 335: Function 'Checksum' not found' 查了一下,发现应该是破解不完全导致C ...
- Java23种设计模式之单例模式的五种实现方式、反射破解单例模式、不能破解枚举单例模式详解
源码链接(Gitee码云):https://gitee.com/oldou/javadesignpatterns 这里有我整理好的Java23种设计模式的源码以及博客教程,博客教程中介绍了Java23 ...
- 写一个饿汉单例模式的例子_你写的单例模式,能防止反序列化和反射吗?
推荐学习 "23种设计模式知识要点"都没读通过,还有脸说摸不清搞不懂? 玩转JAVA筑基之Netty.并发编程与设计模式,打好基础备战春招 前言 说起单例模式,相信大家都不会陌生. ...
- java 防止反射_Java设计模式(一):单例模式,防止反射和反序列化漏洞
一.懒汉式单例模式,解决反射和反序列化漏洞 package com.iter.devbox.singleton; import java.io.ObjectStreamException; impor ...
- 单例模式与反射的攻防之【 道高一尺,魔高一丈 】
文章目录 饿汉模式 没有保护的饿汉模式(可以被反射破坏) 反射破坏普通饿汉模式 带有保护的饿汉模式(构造函数加锁防反射) 反射尝试破坏带有保护的饿汉模式 懒汉模式 基本的懒汉模式(线程不安全) 基本的 ...
- 序列化与反序列化的单例模式_序列化代理模式
序列化与反序列化的单例模式 在上一篇文章中 ,我谈到了一般的序列化. 这是更加集中的内容,并提供了一个细节: 序列化代理模式 . 这是处理序列化中许多问题的一种好方法,通常是最好的方法. 如果开发人员 ...
- java 单例模式 泛型_设计模式之架构设计实例(工厂模式、单例模式、反射、泛型等)...
设计模式, 架构设计实例, 使用到了工厂模式.单例模式.反射.泛型等 项目包结构如下图: 1.bean包 (1)Base.java父类 package test.bean; public class ...
最新文章
- 关于C#调用非托管DLL,报“内存已损坏的”坑,坑,坑
- Android中对Handler用法的总结
- java资料——线性表(转)
- 语言const的生命周期_C语言的角落——这些C语言不常用的特性你知道吗?
- hyperworks2018安装教程
- 改变Android的hello world程序字体颜色和背景颜色
- 直接拿来用!10款实用Android UI工具
- [汇编] 002基础知识-CPU和寄存器
- 搭建cacti 期间问题总结
- Linux电源管理系统架构和驱动(1)-Linux电源管理全局架构
- 今日份教学:怎么压缩PDF文件大小
- Mac-删除自带 ABC 输入法的方法
- 蒙特卡洛树搜索(MTCS)
- 基础项目-家庭记录收支程序
- android应用自启分析与S4启动列表
- 浅析2017年医疗类APP开发前景
- Unity学习笔记(六)---------------GameObject的Active与InActive
- 一键php win10,一键批处理制作纯64位网络骨头版WIN10pe
- 计算机脚本语言是什么?
- 海康摄像头二次开发python_海康摄像头的二次开发(java)
热门文章
- 创建支持nginx服务的docker镜像
- 安卓如何限制横屏和竖屏
- Maven中如何禁止插件(plugin)在子模块(module)上执行
- .htaccess伪静态实例记录
- 配置yum仓库和客户端:
- 多款 Linux 发行版悄然放弃支持 PowerPC
- 深度学习目标检测(YoloV5)项目——从0开始到项目落地部署
- down 网卡端口周期性的up_down 网卡端口周期性的up_思科交换机端口down或up故障原因...
- TopCoder入门教程
- Too many fragmentation in LMT?