成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

破解單例模式:反射、序列化與克隆攻擊的防御之道

開發 前端
對 EnumSingleton 文件進行反編譯,可以發現 EnumSingleton 繼承于 Enum,而 Enum 類確實沒有無參的構造器,所以拋出 NoSuchMethodException。

可能有人看了我上一篇文章里幾種方式對比的表格,覺得枚舉有缺點,為什么Joshua Bloch還推薦使用枚舉?

這就要提到單例的破解了。普通的單例模式是可以通過反射和序列化/反序列化來破解的,而Enum由于自身的特性問題,是無法破解的。當然,由于這種情況基本不會出現,因此我們在使用單例模式的時候也比較少考慮這個問題。

枚舉類是實現單例模式最好的方式

在單例模式的實現中,除去枚舉方法實現的單例模式,其它的實現都可以利用反射構造新的對象,從而破壞單例模式,但是枚舉就不行,下面說說原因:
破壞單例的方式有 3 種,反射、克隆以及序列化,下面詳細介紹:

反射

常見的單例模式實現中,往往有一個私有的構造函數,防止外部程序的調用,但是通過反射可以輕而易舉的破壞這個限制:

public class DobleCheckSingleton {

    private DobleCheckSingleton(){}

    private static volatile DobleCheckSingleton dobleCheckSingleton;

    public static DobleCheckSingleton getSingleton(){
        if (dobleCheckSingleton == null){
            synchronized (DobleCheckSingleton.class){
                if (dobleCheckSingleton == null){
                    dobleCheckSingleton = new DobleCheckSingleton();
                }
            }
        }
        return dobleCheckSingleton;
    }

    public static void main(String[] args) {
        try {
            DobleCheckSingleton dobleCheckSingleton = DobleCheckSingleton.getSingleton();
            Constructor<DobleCheckSingleton> constructor = DobleCheckSingleton.class.getDeclaredConstructor();
            constructor.setAccessible(true);
            DobleCheckSingleton reflectInstance = constructor.newInstance();
            System.out.println(dobleCheckSingleton == reflectInstance);
        } catch (Exception e  e.printStackTrace();
        }
    }
}

輸出:false,單例被破壞

顯然,通過反射可以破壞所有含有無參構造器的單例類,如可以破壞懶漢式、餓漢式、靜態內部類的單例模式。

但是反射無法破壞通過枚舉實現的單例模式,利用反射構造新的對象,由于 enum 沒有無參構造器,結果會拋出 NoSuchMethodException 異常。

public enum EnumSingleton {

    INSTANCE;

    public static void main(String[] args) {
        try {
            EnumSingleton enumSingleton = EnumSingleton.INSTANCE;
            // 獲取無參的構造函數
            Constructor<EnumSingleton> constructor = null;
            constructor = EnumSingleton.class.getDeclaredConstructor();
            // 使用構造函數創建對象
            constructor.setAccessible(true);
            EnumSingleton reflectInstance = constructor.newInstance();
            System.out.println(enumSingleton == reflectInstance);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

輸出:java.lang.NoSuchMethodException: singleton.EnumSingleton.<init>()
    at java.lang.Class.getConstructor0(Class.java:3082)
    at java.lang.Class.getDeclaredConstructor(Class.java:2178)
    at singleton.EnumSingleton.main(EnumSingleton.java:19)

枚舉安全的原因解釋:

對 EnumSingleton 文件進行反編譯,可以發現 EnumSingleton 繼承于 Enum,而 Enum 類確實沒有無參的構造器,所以拋出 NoSuchMethodException。

枚舉類 EnumSingleton 反編譯結果
public final class singleton.EnumSingleton extends java.lang.Enum<singleton.EnumSingleton>

Enum 類的構造方法
protected Enum(String name, int ordinal) {
        this.name = name;
        this.ordinal = ordinal;
}

進一步,通過調用父類有參構造器構造枚舉實例對象,樣例程序又拋出 IllegalArgumentException 異常。

public enum EnumSingleton {

    INSTANCE;

    public EnumSingleton getInstance(){
        return INSTANCE;
    }

    public static void main(String[] args) {
        try {
            EnumSingleton enumSingleton = EnumSingleton.INSTANCE;
            // 獲取無參的構造函數
            Constructor<EnumSingleton> constructor = null;
            constructor = EnumSingleton.class.getDeclaredConstructor(String.class,int.class);
            // 使用構造函數創建對象
            constructor.setAccessible(true);
            EnumSingleton reflectInstance = constructor.newInstance("test",1);
            System.out.println(enumSingleton == reflectInstance);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

輸出:
java.lang.IllegalArgumentException: Cannot reflectively create enum objects
    at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
    at singleton.EnumSingleton.main(EnumSingleton.java:31)

因為 Constructor 的 newInstance 方法限定了 clazz 的類型不能是 enum,否則拋出異常。

@CallerSensitive
 public T newInstance(Object ... initargs)
     throws InstantiationException, IllegalAccessException,
            IllegalArgumentException, InvocationTargetException
 {
    ...
     if ((clazz.getModifiers() & Modifier.ENUM) != 0)
         throw new IllegalArgumentException("Cannot reflectively create enum objects");
     ...
 }

所以枚舉類不能通過反射構建構造函數的方式構建新的實例。

序列化

先看看通過序列化破壞單例的例子,其中 Singleton 實現了 Serializable 接口,才有可能通過序列化破壞單例。

public class DobleCheckSingleton implements Serializable {

    private DobleCheckSingleton() {
    }

    private static volatile DobleCheckSingleton dobleCheckSingleton;

    public static DobleCheckSingleton getSingleton() {
        if (dobleCheckSingleton == null) {
            synchronized (DobleCheckSingleton.class) {
                if (dobleCheckSingleton == null) {
                    dobleCheckSingleton = new DobleCheckSingleton();
                }
            }
        }
        return dobleCheckSingleton;
    }

    public static void main(String[] args) {
        try {
            DobleCheckSingleton dobleCheckSingleton = DobleCheckSingleton.getSingleton();

            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("SerSingleton.obj"));
            oos.writeObject(dobleCheckSingleton);
            oos.flush();
            oos.close();

            FileInputStream fis = new FileInputStream("SerSingleton.obj");
            ObjectInputStream ois = new ObjectInputStream(fis);
            DobleCheckSingleton s1 = (DobleCheckSingleton) ois.readObject();
            ois.close();

            System.out.println(dobleCheckSingleton == s1);
   } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
輸出:false,單例被破壞

枚舉類實現,枚舉類不實現 Serializable 接口,都可以進行序列化,并且返回原來的單例。

public enum EnumSingleton {

    INSTANCE;

    public static void main(String[] args) {
        try {
            EnumSingleton enumSingleton = EnumSingleton.INSTANCE;

            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("SerSingleton.obj"));
            oos.writeObject(enumSingleton);
            oos.flush();
            oos.close();

            FileInputStream fis = new FileInputStream("SerSingleton.obj");
            ObjectInputStream ois = new ObjectInputStream(fis);
            EnumSingleton s1 = (EnumSingleton) ois.readObject();
            ois.close();

            System.out.println(enumSingleton == s1);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

輸出:true

原因: 枚舉類的 writeObject 方法僅僅是將 Enum.name 寫到文件中,反序列化時,根據 readObject 方法的源碼定位到 Enum 的 valueOf 方法,他會根據名稱返回原來的對象。

克隆

實現 Cloneable 接口重寫 clone 方法,但是 Enum 類中 clone 的方法是 final 類型,無法重寫,也就不能通過克隆破壞單例。

public abstract class Enum<E extends Enum<E>>
        implements Comparable<E>, Serializable {
     protected final Object clone() throws CloneNotSupportedException {
        throw new CloneNotSupportedException();
    }
}

不用枚舉如何防止單例模式破壞

若實現了序列化接口,重寫 readResolve 方法即可,反序列化時將調用該方法返回對象實例。

public Object readResolve() throws ObjectStreamException {
    return dobleCheckSingleton;
}

通過反射破壞單例的場景,可以在構造方法中判斷實例是否已經創建,若已創建則拋出異常。

private Singleton(){
    if (instance !=null){
        throw new RuntimeException("實例已經存在,請通過 getInstance()方法獲取");
    }
}

通過clone破壞單例的場景,可以重寫clone方法,返回已有單例對象。


責任編輯:武曉燕 來源: seven97
相關推薦

2011-03-04 09:25:51

Java序列化

2018-03-19 10:20:23

Java序列化反序列化

2012-04-13 10:45:59

XML

2022-09-29 08:39:37

架構

2023-12-13 13:49:52

Python序列化模塊

2013-03-11 13:55:03

JavaJSON

2012-11-30 14:54:48

2011-06-01 15:05:02

序列化反序列化

2022-08-06 08:41:18

序列化反序列化Hessian

2021-06-03 14:14:25

無文件攻擊PowerShell惡意攻擊

2023-11-20 08:44:18

數據序列化反序列化

2012-02-14 09:43:08

2017-05-08 14:33:51

2016-10-09 09:37:49

javascript單例模式

2009-06-14 22:01:27

Java對象序列化反序列化

2009-08-24 17:14:08

C#序列化

2015-02-13 13:11:15

2013-07-27 20:19:14

2011-06-01 14:26:11

序列化

2015-05-20 10:05:10

Ceph分布式文件系統序列化
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 成人影视网址 | 精精精精xxxx免费视频 | 国产精品高清在线 | 亚州精品天堂中文字幕 | 中文字幕在线视频网站 | 亚洲精品一区中文字幕乱码 | 久久免费大片 | 99久久免费精品国产男女高不卡 | 红桃视频一区二区三区免费 | av中文字幕在线观看 | 国产精品久久久久久久久久久久久久 | 亚洲国产偷 | 99re热精品视频 | 国产亚洲精品综合一区 | 中文字幕不卡在线观看 | 亚洲国产精品精华素 | 久久这里只有精品首页 | 欧美色综合一区二区三区 | 国产黄色大片在线免费观看 | 亚洲精品综合 | 91.色| 国产黄色av网站 | 色视频在线观看 | 一区二区三区欧美大片 | 成人一区二区三区在线观看 | 国产成人高清在线观看 | 国产综合一区二区 | 精品一区二区免费视频 | 天天操,夜夜爽 | 天天插天天搞 | 91国产精品 | 中文字幕不卡在线观看 | 中文字幕1区2区 | 美女日批免费视频 | 成人在线观看免费爱爱 | 久久精品中文字幕 | 国产精品久久久久久久久久久新郎 | 日韩精品免费在线观看 | 久久不卡| 一区二区三区四区在线 | 精品国产一区二区三区久久久蜜月 |