日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

单例模式:基于反射和反序列化破解单例模式的漏洞及其解决方法

發(fā)布時間:2025/3/21 编程问答 13 豆豆
生活随笔 收集整理的這篇文章主要介紹了 单例模式:基于反射和反序列化破解单例模式的漏洞及其解决方法 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

單例模式使得在創(chuàng)建類對象的時候只創(chuàng)建一個對象實例。上一節(jié)講解了五種實現(xiàn)單例模式的方式。

分別為:餓漢模式、懶漢模式、double check、靜態(tài)內(nèi)部類、枚舉

但是基于反射和反序列化可以破解單例模式的單一實例,在使用反射時可以通過調(diào)用setAccesible()直接調(diào)用私有構造器,創(chuàng)建新的實例;在反序列化的時候會直接創(chuàng)建新的對象實例。但是以上漏洞只針對前四種方式,枚舉由于是基于JVM底層實現(xiàn)機制,是天然的單例模式。


假設我們使用餓漢的實現(xiàn)方式創(chuàng)建了一個單例類:

package com.test.test1.danlimoshi;public class EHanShi {private static EHanShi eHanShi = new EHanShi();private EHanShi(){}public static EHanShi getInstance(){return eHanShi;} }

接下來基于反射實現(xiàn)創(chuàng)建兩個不同的實例。

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); //跳過檢查機制,直接調(diào)用私有構造器EHanShi eHanShi3 = constructor.newInstance();System.out.println(eHanShi3);} }

打印結(jié)果:顯然創(chuàng)新了新的實例。

com.test.test1.danlimoshi.EHanShi@1b6d3586
com.test.test1.danlimoshi.EHanShi@1b6d3586
com.test.test1.danlimoshi.EHanShi@4554617c

如何解決?

在私有構造器通過拋出異常處理。即當創(chuàng)建第二個實例的時候就刨出異常。

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

接下來基于序列化創(chuàng)建新的實例。

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

打印結(jié)果:顯然創(chuàng)建了新的實例。

com.test.test1.danlimoshi.EHanShi@1b6d3586
com.test.test1.danlimoshi.EHanShi@1b6d3586
com.test.test1.danlimoshi.EHanShi@6d03e736

如何解決?

在單例類中創(chuàng)建一個方法readResolve(),基于回調(diào)機制,在反序列化的時候直接會調(diào)用這個方法。返回當前實例。

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

那么就不會創(chuàng)建新的實例了。


測試五種實現(xiàn)方式的耗時:

package com.test.test1.danlimoshi;import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit;//測試多線程環(huán)境下實現(xiàn)這幾種方式的耗時 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個線程執(zhí)行完成,也即是計數(shù)器值為0,main線程繼續(xù)往下執(zhí)行long endTime = System.currentTimeMillis();System.out.println("總耗時:"+(endTime-startTime));}} 《新程序員》:云原生和全面數(shù)字化實踐50位技術專家共同創(chuàng)作,文字、視頻、音頻交互閱讀

總結(jié)

以上是生活随笔為你收集整理的单例模式:基于反射和反序列化破解单例模式的漏洞及其解决方法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。