Java設計模式(一):單例模式,防止反射和反序列化漏洞
阿新 • • 發佈:2018-12-26
package com.iter.devbox.singleton; import java.io.ObjectStreamException; import java.io.Serializable; /** * 靜態內部類實現方式(也是一種懶載入方式) * 這種方式:執行緒安全,呼叫效率高,並且實現了延遲載入 * 解決反射和反序列化漏洞 * @author Shearer * */ public class SingletonDemo7 implements Serializable{ private static class SingletonClassInstance { private static final SingletonDemo7 instance = new SingletonDemo7(); } // 方法沒有同步,呼叫效率高 public static SingletonDemo7 getInstance() { return SingletonClassInstance.instance; } // 防止反射獲取多個物件的漏洞 private SingletonDemo7() { if (null != SingletonClassInstance.instance) throw new RuntimeException(); } // 防止反序列化獲取多個物件的漏洞 private Object readResolve() throws ObjectStreamException { return SingletonClassInstance.instance; } } package com.iter.devbox.singleton; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Constructor; public class Client3 { public static void main(String[] args) throws Exception { SingletonDemo7 sc1 = SingletonDemo7.getInstance(); SingletonDemo7 sc2 = SingletonDemo7.getInstance(); System.out.println(sc1); // sc1,sc2是同一個物件 System.out.println(sc2); // 通過反射的方式直接呼叫私有構造器(通過在構造器裡丟擲異常可以解決此漏洞) Class<SingletonDemo7> clazz = (Class<SingletonDemo7>) Class.forName("com.iter.devbox.singleton.SingletonDemo7"); Constructor<SingletonDemo7> c = clazz.getDeclaredConstructor(null); c.setAccessible(true); // 跳過許可權檢查 SingletonDemo7 sc3 = c.newInstance(); SingletonDemo7 sc4 = c.newInstance(); System.out.println("通過反射的方式獲取的物件sc3:" + sc3); // sc3,sc4不是同一個物件 System.out.println("通過反射的方式獲取的物件sc4:" + sc4); // 通過反序列化的方式構造多個物件(類需要實現Serializable介面) // 1. 把物件sc1寫入硬碟檔案 FileOutputStream fos = new FileOutputStream("object.out"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(sc1); oos.close(); fos.close(); // 2. 把硬碟檔案上的物件讀出來 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.out")); // 如果物件定義了readResolve()方法,readObject()會呼叫readResolve()方法。從而解決反序列化的漏洞 SingletonDemo7 sc5 = (SingletonDemo7) ois.readObject(); // 反序列化出來的物件,和原物件,不是同一個物件。如果物件定義了readResolve()方法,可以解決此問題。 System.out.println("物件定義了readResolve()方法,通過反序列化得到的物件:" + sc5); ois.close(); } }