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.
绕过了单例的限制,构造了第二个实例。code
除了用序列化/反序列化攻击外,还能够用反射攻击。对象
然而我只须要将这个单例类JerrySingleton的构造函数经过反射设置成能够访问Accessible,而后就能经过反射调用该构造函数,进而生成新的对象实例。这样就破坏了单例模式。blog
第6行代码会打印false。get
针对这种攻击,一种可行的防护措施是在单例类的构造函数内定义一个布尔变量,初始化为false。当构造函数执行后,该变量被置为true。若是接下来构造函数再次被执行,则人为抛出异常,避免构造函数重复执行。string
这种防护措施没法从根本上杜绝Singleton被攻击,由于攻击者仍旧能够经过反射来修改布尔变量flag的值,从而绕过这个检查。it
最理想的不会受到攻击的单例模式实现是借助Java里枚举类Enumeration的特性:io
这种实现类型的单例模式的消费代码:class
System.out.println("Name:" + JerrySingletonAnotherApproach.INSTANCE.getName());变量
若是攻击者经过前面介绍的反射代码对这种实现方式的单例进行攻击,JDK会抛出NoSuchMethodException异常:
究其缘由,是由于如今咱们是经过Java枚举方式实现的单例,枚举类没有传统意义上的构造函数,所以对这种反射攻击免疫。
要获取更多Jerry的原创文章,请关注公众号"汪子熙":