CLASS zcl_jerry_singleton DEFINITION PUBLIC FINAL CREATE PRIVATE . PUBLIC SECTION. INTERFACES if_serializable_object . CLASS-METHODS class_constructor . CLASS-METHODS get_instance RETURNING VALUE(ro_instance) TYPE REF TO zcl_jerry_singleton . PROTECTED SECTION. PRIVATE SECTION. CLASS-DATA so_instance TYPE REF TO zcl_jerry_singleton . DATA mv_name TYPE string . DATA mv_initialized TYPE abap_bool . METHODS constructor . ENDCLASS. CLASS ZCL_JERRY_SINGLETON IMPLEMENTATION. * <SIGNATURE>---------------------------------------------------------------------------------------+ * | Static Public Method ZCL_JERRY_SINGLETON=>CLASS_CONSTRUCTOR * +-------------------------------------------------------------------------------------------------+ * +--------------------------------------------------------------------------------------</SIGNATURE> METHOD class_constructor. so_instance = NEW zcl_jerry_singleton( ). ENDMETHOD. * <SIGNATURE>---------------------------------------------------------------------------------------+ * | Instance Public Method ZCL_JERRY_SINGLETON->CONSTRUCTOR * +-------------------------------------------------------------------------------------------------+ * +--------------------------------------------------------------------------------------</SIGNATURE> METHOD constructor. mv_name = 'Jerry'. IF mv_initialized = abap_false. mv_initialized = abap_true. ELSE. MESSAGE 'you are in trouble!' TYPE 'E' DISPLAY LIKE 'I'. ENDIF. ENDMETHOD. * <SIGNATURE>---------------------------------------------------------------------------------------+ * | Static Public Method ZCL_JERRY_SINGLETON=>GET_INSTANCE * +-------------------------------------------------------------------------------------------------+ * | [<-()] RO_INSTANCE TYPE REF TO ZCL_JERRY_SINGLETON * +--------------------------------------------------------------------------------------</SIGNATURE> METHOD get_instance. ro_instance = so_instance. ENDMETHOD. ENDCLASS.
經過序列化/反序列化攻擊單例模式:函數
DATA(lo_instance) = zcl_jerry_singleton=>get_instance( ). DATA: s TYPE string. CALL TRANSFORMATION id SOURCE model = lo_instance RESULT XML s. DATA: lo_instance2 TYPE REF TO zcl_jerry_singleton. CALL TRANSFORMATION id SOURCE XML s RESULT model = lo_instance2.
繞過了單例的限制,構造了第二個實例。spa
除了用序列化/反序列化攻擊外,還能夠用反射攻擊。code
然而我只須要將這個單例類JerrySingleton的構造函數經過反射設置成能夠訪問Accessible,而後就能經過反射調用該構造函數,進而生成新的對象實例。這樣就破壞了單例模式。對象
第6行代碼會打印false。blog
針對這種攻擊,一種可行的防護措施是在單例類的構造函數內定義一個布爾變量,初始化爲false。當構造函數執行後,該變量被置爲true。若是接下來構造函數再次被執行,則人爲拋出異常,避免構造函數重複執行。rem
這種防護措施沒法從根本上杜絕Singleton被攻擊,由於攻擊者仍舊能夠經過反射來修改布爾變量flag的值,從而繞過這個檢查。get
最理想的不會受到攻擊的單例模式實現是藉助Java裏枚舉類Enumeration的特性:string
這種實現類型的單例模式的消費代碼:it
System.out.println("Name:" + JerrySingletonAnotherApproach.INSTANCE.getName());io
若是攻擊者經過前面介紹的反射代碼對這種實現方式的單例進行攻擊,JDK會拋出NoSuchMethodException異常:
究其緣由,是由於如今咱們是經過Java枚舉方式實現的單例,枚舉類沒有傳統意義上的構造函數,所以對這種反射攻擊免疫。
要獲取更多Jerry的原創文章,請關注公衆號"汪子熙":