单例模式,防止反射和反序列化漏洞

一、懒汉式单例模式,解决反射和反序列化漏洞

 
  1. package com.iter.devbox.singleton;

  2.  
  3. import java.io.ObjectStreamException;

  4. import java.io.Serializable;

  5.  
  6. /**

  7. * 懒汉式(如何防止反射和反序列化漏洞)

  8. * @author Shearer

  9. *

  10. */

  11. public class SingletonDemo6 implements Serializable{

  12.  
  13. // 类初始化时,不初始化这个对象(延迟加载,真正用的时候再创建)

  14. private static SingletonDemo6 instance;

  15.  
  16. private SingletonDemo6() {

  17. // 防止反射获取多个对象的漏洞

  18. if (null != instance) {

  19. throw new RuntimeException();

  20. }

  21. }

  22.  
  23. // 方法同步,调用效率低

  24. public static synchronized SingletonDemo6 getInstance() {

  25. if (null == instance)

  26. instance = new SingletonDemo6();

  27. return instance;

  28. }

  29.  
  30. // 防止反序列化获取多个对象的漏洞。

  31. // 无论是实现Serializable接口,或是Externalizable接口,当从I/O流中读取对象时,readResolve()方法都会被调用到。

  32. // 实际上就是用readResolve()中返回的对象直接替换在反序列化过程中创建的对象。

  33. private Object readResolve() throws ObjectStreamException {

  34. return instance;

  35. }

  36. }

  37.  
  38.  
  39. package com.iter.devbox.singleton;

  40.  
  41. import java.io.FileInputStream;

  42. import java.io.FileOutputStream;

  43. import java.io.ObjectInputStream;

  44. import java.io.ObjectOutputStream;

  45.  
  46. public class Client2 {

  47.  
  48. public static void main(String[] args) throws Exception {

  49. SingletonDemo6 sc1 = SingletonDemo6.getInstance();

  50. SingletonDemo6 sc2 = SingletonDemo6.getInstance();

  51. System.out.println(sc1); // sc1,sc2是同一个对象

  52. System.out.println(sc2);

  53.  
  54. // 通过反射的方式直接调用私有构造器(通过在构造器里抛出异常可以解决此漏洞)

  55. /* Class<SingletonDemo6> clazz = (Class<SingletonDemo6>) Class.forName("com.iter.devbox.singleton.SingletonDemo6");

  56. Constructor<SingletonDemo6> c = clazz.getDeclaredConstructor(null);

  57. c.setAccessible(true); // 跳过权限检查

  58. SingletonDemo6 sc3 = c.newInstance();

  59. SingletonDemo6 sc4 = c.newInstance();

  60. System.out.println(sc3); // sc3,sc4不是同一个对象

  61. System.out.println(sc4);*/

  62.  
  63. // 通过反序列化的方式构造多个对象(类需要实现Serializable接口)

  64.  
  65. // 1. 把对象sc1写入硬盘文件

  66. FileOutputStream fos = new FileOutputStream("object.out");

  67. ObjectOutputStream oos = new ObjectOutputStream(fos);

  68. oos.writeObject(sc1);

  69. oos.close();

  70. fos.close();

  71.  
  72. // 2. 把硬盘文件上的对象读出来

  73. ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.out"));

  74. // 如果对象定义了readResolve()方法,readObject()会调用readResolve()方法。从而解决反序列化的漏洞

  75. SingletonDemo6 sc5 = (SingletonDemo6) ois.readObject();

  76. // 反序列化出来的对象,和原对象,不是同一个对象。如果对象定义了readResolve()方法,可以解决此问题。

  77. System.out.println(sc5);

  78. ois.close();

  79. }

  80.  
  81. }

二、静态内部类式单例模式(解决反射和反序列化漏洞)

 
  1. package com.iter.devbox.singleton;

  2.  
  3. import java.io.ObjectStreamException;

  4. import java.io.Serializable;

  5.  
  6. /**

  7. * 静态内部类实现方式(也是一种懒加载方式)

  8. * 这种方式:线程安全,调用效率高,并且实现了延迟加载

  9. * 解决反射和反序列化漏洞

  10. * @author Shearer

  11. *

  12. */

  13. public class SingletonDemo7 implements Serializable{

  14.  
  15. private static class SingletonClassInstance {

  16. private static final SingletonDemo7 instance = new SingletonDemo7();

  17. }

  18.  
  19. // 方法没有同步,调用效率高

  20. public static SingletonDemo7 getInstance() {

  21. return SingletonClassInstance.instance;

  22. }

  23.  
  24. // 防止反射获取多个对象的漏洞

  25. private SingletonDemo7() {

  26. if (null != SingletonClassInstance.instance)

  27. throw new RuntimeException();

  28. }

  29.  
  30. // 防止反序列化获取多个对象的漏洞

  31. private Object readResolve() throws ObjectStreamException {

  32. return SingletonClassInstance.instance;

  33. }

  34. }

  35.  
  36.  
  37. package com.iter.devbox.singleton;

  38.  
  39. import java.io.FileInputStream;

  40. import java.io.FileOutputStream;

  41. import java.io.ObjectInputStream;

  42. import java.io.ObjectOutputStream;

  43. import java.lang.reflect.Constructor;

  44.  
  45. public class Client3 {

  46.  
  47. public static void main(String[] args) throws Exception {

  48. SingletonDemo7 sc1 = SingletonDemo7.getInstance();

  49. SingletonDemo7 sc2 = SingletonDemo7.getInstance();

  50. System.out.println(sc1); // sc1,sc2是同一个对象

  51. System.out.println(sc2);

  52.  
  53. // 通过反射的方式直接调用私有构造器(通过在构造器里抛出异常可以解决此漏洞)

  54. Class<SingletonDemo7> clazz = (Class<SingletonDemo7>) Class.forName("com.iter.devbox.singleton.SingletonDemo7");

  55. Constructor<SingletonDemo7> c = clazz.getDeclaredConstructor(null);

  56. c.setAccessible(true); // 跳过权限检查

  57. SingletonDemo7 sc3 = c.newInstance();

  58. SingletonDemo7 sc4 = c.newInstance();

  59. System.out.println("通过反射的方式获取的对象sc3:" + sc3); // sc3,sc4不是同一个对象

  60. System.out.println("通过反射的方式获取的对象sc4:" + sc4);

  61.  
  62. // 通过反序列化的方式构造多个对象(类需要实现Serializable接口)

  63.  
  64. // 1. 把对象sc1写入硬盘文件

  65. FileOutputStream fos = new FileOutputStream("object.out");

  66. ObjectOutputStream oos = new ObjectOutputStream(fos);

  67. oos.writeObject(sc1);

  68. oos.close();

  69. fos.close();

  70.  
  71. // 2. 把硬盘文件上的对象读出来

  72. ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.out"));

  73. // 如果对象定义了readResolve()方法,readObject()会调用readResolve()方法。从而解决反序列化的漏洞

  74. SingletonDemo7 sc5 = (SingletonDemo7) ois.readObject();

  75. // 反序列化出来的对象,和原对象,不是同一个对象。如果对象定义了readResolve()方法,可以解决此问题。

  76. System.out.println("对象定义了readResolve()方法,通过反序列化得到的对象:" + sc5);

  77. ois.close();

  78. }

  79.  
  80. }

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hardwin/article/details/51477359

猜你喜欢

转载自blog.csdn.net/sinianliushui/article/details/81286872
今日推荐