public static void main(String[] args) throws Exception { SingletonDemo1 sc1 = SingletonDemo1.getInstance(); SingletonDemo1 sc2 = SingletonDemo1.getInstance(); System.out.println(sc1); // sc1,sc2是同一个对象 System.out.println(sc2); // 通过反射的方式直接调用私有构造器(通过在构造器里抛出异常可以解决此漏洞) // Class<SingletonDemo1> clazz = (Class<SingletonDemo1>) Class.forName("com.sankuai.ia.demo.web.service.SingletonDemo1"); // Constructor<SingletonDemo1> c = clazz.getDeclaredConstructor(null); // c.setAccessible(true); // 跳过权限检查 // SingletonDemo1 sc3 = c.newInstance(); // SingletonDemo1 sc4 = c.newInstance(); // System.out.println(sc3); // sc3,sc4不是同一个对象 // System.out.println(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()方法。从而解决反序列化的漏洞 SingletonDemo1 sc5 = (SingletonDemo1) ois.readObject(); // 反序列化出来的对象,和原对象,不是同一个对象。如果对象定义了readResolve()方法,可以解决此问题。 System.out.println(sc5); ois.close(); }
package com.sankuai.ia.demo.web.service; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamException; import java.io.Serializable; import java.lang.reflect.Constructor; /** * * * 静态内部类实现方式(也是一种懒加载方式) * 这种方式:线程安全,调用效率高,并且实现了延迟加载 * 解决反射和反序列化漏洞 * **/ public class SingletonDemo2 implements Serializable{ private static class SingletonClassInstance { private static final SingletonDemo2 instance = new SingletonDemo2(); } // 方法没有同步,调用效率高 public static SingletonDemo2 getInstance() { return SingletonClassInstance.instance; } // 防止反射获取多个对象的漏洞 private SingletonDemo2() { if (null != SingletonClassInstance.instance) throw new RuntimeException(); } // 防止反序列化获取多个对象的漏洞 private Object readResolve() throws ObjectStreamException { return SingletonClassInstance.instance; } public static void main(String[] args) throws Exception { SingletonDemo2 sc1 = SingletonDemo2.getInstance(); SingletonDemo2 sc2 = SingletonDemo2.getInstance(); System.out.println(sc1); // sc1,sc2是同一个对象 System.out.println(sc2); // 通过反射的方式直接调用私有构造器(通过在构造器里抛出异常可以解决此漏洞) Class<SingletonDemo2> clazz = (Class<SingletonDemo2>) Class.forName("com.sankuai.ia.demo.web.service.SingletonDemo2"); Constructor<SingletonDemo2> c = clazz.getDeclaredConstructor(null); c.setAccessible(true); // 跳过权限检查 SingletonDemo2 sc3 = c.newInstance(); SingletonDemo2 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()方法。从而解决反序列化的漏洞 SingletonDemo2 sc5 = (SingletonDemo2) ois.readObject(); // 反序列化出来的对象,和原对象,不是同一个对象。如果对象定义了readResolve()方法,可以解决此问题。 System.out.println("对象定义了readResolve()方法,通过反序列化得到的对象:" + sc5); ois.close(); } }
原文:https://www.cnblogs.com/cjn123/p/12159536.html