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


一、懶漢式單例模式,解決反射和反序列化漏洞

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM