如何防止单例模式被JAVA反射攻击

    单例模式相信大家都知道,用过的人不在少数。之前写过一篇博文《singleton模式四种线程安全的实现》(参见:http://blog.csdn.net/u013256816/article/details/50427061),讲诉了单例模式的四种写法,并指出占位符模式的写法比较ok,详见如下:

package com.effective.singleton;

public class Elvis
{
    private static boolean flag = false;

    private Elvis(){
    }

    private  static class SingletonHolder{
        private static final Elvis INSTANCE = new Elvis();
    }

    public static Elvis getInstance()
    {
        return SingletonHolder.INSTANCE;
    }

    public void doSomethingElse()
    {

    }
}

    但这都是基于一个条件:确保不会通过反射机制调用私有的构造器。
    这里举个例子,通过JAVA的反射机制来“攻击”单例模式:

package com.effective.singleton;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class ElvisReflectAttack
{

    public static void main(String[] args) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException
    {
        Class<?> classType = Elvis.class;

        Constructor<?> c = classType.getDeclaredConstructor(null);
        c.setAccessible(true);
        Elvis e1 = (Elvis)c.newInstance();
        Elvis e2 = Elvis.getInstance();
        System.out.println(e1==e2);
    }

}

   运行结果:false
   可以看到,通过反射获取构造函数,然后调用setAccessible(true)就可以调用私有的构造函数,所有e1和e2是两个不同的对象。
   如果要抵御这种攻击,可以修改构造器,让它在被要求创建第二个实例的时候抛出异常。
   经修改后:

package com.effective.singleton;

public class ElvisModified
{
    private static boolean flag = false;

    private ElvisModified(){
        synchronized(ElvisModified.class)
        {
            if(flag == false)
            {
                flag = !flag;
            }
            else
            {
                throw new RuntimeException("单例模式被侵犯!");
            }
        }
    }

    private  static class SingletonHolder{
        private static final ElvisModified INSTANCE = new ElvisModified();
    }

    public static ElvisModified getInstance()
    {
        return SingletonHolder.INSTANCE;
    }

    public void doSomethingElse()
    {

    }
}

    测试代码:

package com.effective.singleton;

import java.lang.reflect.Constructor;

public class ElvisModifiedReflectAttack
{

    public static void main(String[] args)
    {
        try
        {
            Class<ElvisModified> classType = ElvisModified.class;

            Constructor<ElvisModified> c = classType.getDeclaredConstructor(null);
            c.setAccessible(true);
            ElvisModified e1 = (ElvisModified)c.newInstance();
            ElvisModified e2 = ElvisModified.getInstance();
            System.out.println(e1==e2);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}

    运行结果:

Exception in thread "main" java.lang.ExceptionInInitializerError
    at com.effective.singleton.ElvisModified.getInstance(ElvisModified.java:27)
    at com.effective.singleton.ElvisModifiedReflectAttack.main(ElvisModifiedReflectAttack.java:17)
Caused by: java.lang.RuntimeException: 单例模式被侵犯!
    at com.effective.singleton.ElvisModified.<init>(ElvisModified.java:16)
    at com.effective.singleton.ElvisModified.<init>(ElvisModified.java:7)
    at com.effective.singleton.ElvisModified$SingletonHolder.<clinit>(ElvisModified.java:22)
    ... 2 more

    可以看到,成功的阻止了单例模式被破坏。
    从JDK1.5开始,实现Singleton还有新的写法,只需编写一个包含单个元素的枚举类型。推荐写法:

package com.effective.singleton;

public enum SingletonClass
{
    INSTANCE;

    public void test()
    {
        System.out.println("The Test!");
    }
}

    测试代码:

package com.effective;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

import com.effective.singleton.SingletonClass;

public class TestMain
{

    public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException
    {
        Class<SingletonClass> classType = SingletonClass.class;
        Constructor<SingletonClass> c = (Constructor<SingletonClass>) classType.getDeclaredConstructor();
        c.setAccessible(true);
        c.newInstance();
    }
}

    运行结果:

Exception in thread "main" java.lang.NoSuchMethodException: com.effective.singleton.SingletonClass.<init>()
    at java.lang.Class.getConstructor0(Unknown Source)
    at java.lang.Class.getDeclaredConstructor(Unknown Source)
    at com.effective.TestMain.main(TestMain.java:22)

    由此可见这种写法也可以防止单例模式被“攻击”。
    而且这种写法也可以防止序列化破坏单例模式,具体不在举例了,有关序列化以及单例模式被序列化破坏可以参考博文《JAVA序列化》(链接:http://blog.csdn.net/u013256816/article/details/50474678)。
    单元素的枚举类型已经成为实现Singleton模式的最佳方法。

时间: 2024-09-12 04:18:36

如何防止单例模式被JAVA反射攻击的相关文章

Java反射机制的学习

Java反射机制是Java语言被视为准动态语言的关键性质.Java反射机制的核心就是允许在运行时通过Java Reflection APIs来取得已知名字的class类的相关信息,动态地生成此类,并调用其方法或修改其域(甚至是本身声明为private的域或方法). 也许你使用Java已经很长时间了,可是几乎不会用到Java反射机制.你会嗤之以鼻地告诉我,Java反射机制没啥用.或许在J2EE.J2SE等平台,Java反射机制没啥用(具体我也不了解,不多做评论),但是在Android应用开发中,该

Java反射机制:包括组成、结构和示例说明等内容

第1部分 Java 反射机制介绍 Java 反射机制.通俗来讲呢,就是在运行状态中,我们可以根据"类的部分已经的信息"来还原"类的全部的信息".这里"类的部分已经的信息",可以是"类名"或"类的对象"等信息."类的全部信息"就是指"类的属性,方法,继承关系和Annotation注解"等内容. 举个简单的例子:假设对于类ReflectionTest.java,我们知道的

java反射机制系列(二)例子

下面我以顾客买相机为例来说明Java反射机制的应用.例子中涉及的类和接口有: Camera接口:定义了takePhoto()方法. Camera01类:一种照相机的类型,实现Camera接口. Camera02类:另一种照相机的类型,实现Camera接口. Seller类:卖照相机. Customer类:买相机,有main方法. 所有类都放在com包里 程序如下: public interface Camera { //声明照相机必须可以拍照 public void takePhoto(); }

java 反射机制系列(一) 初识Java Reflection

Java 反射机制是指Java程序可以在执行期载入,探知,使用编译期间完全未知的classes.这句话可能有点难以理解,我们可以通过一个例子来看.在Java程序中我们经常会用到这样一条语句来创建一个对象.Date date = new Date();在这条语句中date的类型(Java.util.Date)在编译时 已经确定.那么,有没有办法使我们把对象类型的确定时间由编译转到运行,答案是肯定的.这就是Java反射机制所提供的便利.而且它不单单可以生成对象还可以获取Field,对Field设值,

使用Java反射机制确定基本数据类型属性

Java反射机制提供了一种强大的应用程序接口来帮助我们在运行时检测和执行对象上的操作.该机制允许程序员审视一个未知对象,并且获得它的属性,例如对象上的方法,JavaBean的属性以及继承关系等. Java中有两种类型的值,一种是类类型,它把对象的属性定义在一个类中.类类型是面向对象结构的基本组成部分.第二种类型的值是基本数据类型.反射类型通常用于类类型,但是,其实它也可以很方便应用到Java的基本类型上. Java中的基本类型继承自基于C的语法,其中包括boolean.byte.short.in

初学java反射的问题 一直抛出classnotfoundexception

问题描述 初学java反射的问题 一直抛出classnotfoundexception public class TestReflection { public static void main(String[] args) { try{ String str = ""T""; Class c = Class.forName(str); c.newInstance(); } catch (ClassNotFoundException e) { e.printStac

Java 反射之动态代理

利用Java反射机制你可以在运行期动态的创建接口的实现.java.lang.reflect.Proxy类就可以实现这一功能.这个类的名字(译者注:Proxy意思为代理)就是为什么把动态接口实现叫做动态代理.动态的代理的用途十分广泛,比如数据库连接和事物管理(transaction management)还有单元测试时用到的动态mock对象以及AOP中的方法拦截功能等等都使用到了动态代理. 创建代理你可以通过使用Proxy.newProxyInstance()方法创建动态代理.newProxyIn

继承-关于java反射的问题!

问题描述 关于java反射的问题! RT: 例如:有一个工具类BaseUtil 方法:public class BaseUtil 一个java普通类:User 继承 BaseUtil 方法 public class User() extends BaseUtil<User>{}; 问题来了 我在BaseUtil类里如何获取继承它本身的类实例?(运行时) 解决方案 浅谈java反射问题java反射里的数组问题Java反射相关问题 解决方案二: 通过反射可以取得 本类的构造器,属性, 权限修饰符

Java反射在JVM的实现

本文目录 什么是Java反射,有什么用? Java Class文件的结构 Java Class加载的过程 反射在native的实现 附录 1. 什么是Java反射,有什么用? 反射使程序代码能够接入装载到JVM中的类的内部信息,允许在编写与执行时,而不是源代码中选定的类协作的代码,是以开发效率换运行效率的一种手段.这使反射成为构建灵活应用的主要工具. 反射可以: 调用一些私有方法,实现黑科技.比如双卡短信发送.设置状态栏颜色.自动挂电话等. 实现序列化与反序列化,比如PO的ORM,Json解析等