如何通过反射访问私有方法和私有数据成员?

时间:2012-07-14 12:14:58

标签: java security reflection private

我知道我们可以通过反思访问私有构造函数,如我在回答我的问题时提到的@Sanjay T. SharmaDoes “instanceof Void” always return false?

但是,@duffymo said

  

您可以使用反射访问私有内容 - 方法,构造函数,数据成员,所有内容。

  1. 如何访问私有方法和私有数据成员?
  2. 是否可以通过反射访问本地变量?
  3. 有没有办法阻止任何人访问私有构造函数,方法和数据成员?

6 个答案:

答案 0 :(得分:66)

1) 如何访问私有方法和私有数据成员?

您可以在setAccessible(true)方法的帮助下完成此操作:

class Dummy{
    private void foo(){
        System.out.println("hello foo()");
    }
    private int i = 10;
}

class Test{
    public static void main(String[] args) throws Exception {
        Dummy d = new Dummy();

        /*---  [INVOKING PRIVATE METHOD]  ---*/
        Method m = Dummy.class.getDeclaredMethod("foo");
        //m.invoke(d); // Exception java.lang.IllegalAccessException
        m.setAccessible(true);//Abracadabra
        m.invoke(d); // Now it's OK

        /*---  [GETING VALUE FROM PRIVATE FIELD]  ---*/
        Field f = Dummy.class.getDeclaredField("i");
        //System.out.println(f.get(d)); // Not accessible now
        f.setAccessible(true); // Abracadabra
        System.out.println(f.get(d)); // Now it's OK

        /*---  [SETTING VALUE OF PRIVATE FIELD]  ---*/
        Field f2 = Dummy.class.getDeclaredField("i");
        //f2.set(d,20); // Not accessible now
        f2.setAccessible(true); // Abracadabra
        f2.set(d, 20); // Now it's OK
        System.out.println(f2.get(d));
    }
}

2) 是否可以通过反射访问本地变量?

没有。局部变量不能在创建它们的块之外访问(有人可以说你可以将这样的变量分配给像field = localVariable;这样的字段,然后通过反射访问这样的字段,但这样我们将访问,而不是变量)。

3) 有没有办法阻止任何人访问私有构造函数,方法和数据成员?

我认为对于constructorsmethods,您可以使用stacktrace来检查它是否由Reflection调用。
对于字段,我找不到阻止通过反射访问它们的解决方案。

[警告:未经任何人批准。我只是根据你的问题写了它。]

class Dummy {
    private void safeMethod() {
        StackTraceElement[] st = new Exception().getStackTrace();
        // If a method was invoked by reflection, the stack trace would be similar
        // to something like this:
        /*
        java.lang.Exception
            at package1.b.Dummy.safeMethod(SomeClass.java:38)
            at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
            at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
            at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        ->    at java.lang.reflect.Method.invoke(Method.java:601)
            at package1.b.Test.main(SomeClass.java:65)
        */
        //5th line marked by "->" is interesting one so I will try to use that info

        if (st.length > 5 &&
            st[4].getClassName().equals("java.lang.reflect.Method"))
            throw new RuntimeException("safeMethod() is accessible only by Dummy object");

        // Now normal code of method
        System.out.println("code of safe method");
    }

    // I will check if it is possible to normally use that method inside this class
    public void trySafeMethod(){
        safeMethod();
    }

    Dummy() {
        safeMethod();
    }
}

class Dummy1 extends Dummy {}

class Test {
    public static void main(String[] args) throws Exception {
        Dummy1 d1 = new Dummy1(); // safeMethod can be invoked inside a superclass constructor
        d1.trySafeMethod(); // safeMethod can be invoked inside other Dummy class methods
        System.out.println("-------------------");

        // Let's check if it is possible to invoke it via reflection
        Method m2 = Dummy.class.getDeclaredMethod("safeMethod");
        // m.invoke(d);//exception java.lang.IllegalAccessException
        m2.setAccessible(true);
        m2.invoke(d1);
    }
}

Test主要方法的输出

code of safe method
code of safe method
-------------------
Exception in thread "main" java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at package1.b.Test.main(MyClass2.java:87)
Caused by: java.lang.RuntimeException: method safeMethod() is accessible only by Dummy object
    at package1.b.Dummy.safeMethod(MyClass2.java:54)
    ... 5 more

答案 1 :(得分:9)

  1. 使用您链接到的答案中显示的方法:setAccessible(true),这是Field,Constructor和Method的超类的方法。
  2. 没有
  3. 不,除非代码在您控制的JVM中运行,否则您将在其中安装安全管理器。但是如果你给某人一个jar文件,并且他使用这个jar文件中的类,他将能够访问所有内容。

答案 2 :(得分:3)

要访问 私人字段,您需要致电 Class.getDeclaredField(String name)enter code here方法。 检查这个简单的代码:

public class PrivateObject {

  private String privateString = null;

  public PrivateObject(String privateString) {
    this.privateString = privateString;
  }
}

PrivateObject privateObject = new PrivateObject("The Private Value");

Field privateStringField = PrivateObject.class.
            getDeclaredField("privateString");

privateStringField.setAccessible(true);

String fieldValue = (String) privateStringField.get(privateObject);
System.out.println("fieldValue = " + fieldValue

要访问私有方法,您需要调用Class.getDeclaredMethod(String name,Class [] parameterTypes)或Class.getDeclaredMethods()方法。

检查以下简单代码:

public class PrivateObject {

  private String privateString = null;

  public PrivateObject(String privateString) {
    this.privateString = privateString;
  }

  private String getPrivateString(){
    return this.privateString;
  }
}
PrivateObject privateObject = new PrivateObject("The Private Value");

Method privateStringMethod = PrivateObject.class.
        getDeclaredMethod("getPrivateString", null);

privateStringMethod.setAccessible(true);

String returnValue = (String)
        privateStringMethod.invoke(privateObject, null);

System.out.println("returnValue = " + returnValue);

详细阅读 http://tutorials.jenkov.com/java-reflection/private-fields-and-methods.html

答案 3 :(得分:1)

 Area s=(Area)c.newInstance();  
 s.setRadius(10);
 System.out.println("Area: "+s.calculateArea(4));

 Method m[]=c.getDeclaredMethods(); 
  Constructor c1[]=c.getConstructors();  

 for(int i=0;i<m.length;i++)
     System.out.println(""+m[i]);

 for(int i=0;i<c1.length;i++)
     System.out.println(""+c1[i]);

答案 4 :(得分:1)

示例如下:

import java.lang.reflect.Constructor;

import java.lang.reflect.Field;

import java.lang.reflect.InvocationTargetException;

import java.lang.reflect.Method;

class Test
{
    private int a = 5;   // Private data member

    private void call(int n) // Private method
    {
        System.out.println("in call()  n: " + n);
    }
}

public class Sample
{
    public static void main(String args[]) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException
    {
        Class c = Class.forName("Test");

        Object obj = c.newInstance();

        //---- Accessing a private method
        Method m=c.getDeclaredMethod("call",new Class[]{int.class});
        m.setAccessible(true);
        m.invoke(obj,7);

       //---- Accessing a private data member
       Field d = c.getDeclaredField("a");
       d.setAccessible(true);
       System.out.println(d.getInt(obj));
    }
}

答案 5 :(得分:0)

回答你的第三个问题:

  1. 有没有办法阻止任何人访问私有构造函数,方法和数据成员?
  2. 答案:

    是的,您可以限制访问权限(当有人试图访问您的私有构造函数/方法/数据时,您可以抛出异常)

    请参阅以下示例:

    ******JavaSingleton Class******
    
    package server;
    
    public class JavaSingleton {
    
      private static final JavaSingleton INSTANCE = new JavaSingleton();
    
      private JavaSingleton() {
        if (INSTANCE != null) {
          throw new IllegalStateException("Inside JavaSingleton(): JavaSingleton " +
                                                            "instance already created.");
        }
        System.out.println("Inside JavaSingleton(): Singleton instance is being created.");
      }
    
      public static final JavaSingleton getInstance() {
        return INSTANCE;
      }
    }
    
    
    ***Listing 2: JavaSingleton client***
    
    import server.JavaSingleton;
    import java.lang.reflect.*;
    
    public class TestSingleton {
    
      public static void main(String[] args) throws ReflectiveOperationException {
        System.out.println("Inside main(): Getting the singleton instance using getInstance()...");
        JavaSingleton s = JavaSingleton.getInstance();
    
        System.out.println("Inside main(): Trying to use reflection to get another instance...");
        Class<JavaSingleton> clazz = JavaSingleton.class;
        Constructor<JavaSingleton> cons = clazz.getDeclaredConstructor();
        cons.setAccessible(true);
        JavaSingleton s2 = cons.newInstance();
      }
    }
    
    Output:
    
    C:\singleton>java TestSingleton
    Inside main(): Getting the singleton instance using getInstance()...
    Inside JavaSingleton(): Singleton instance is being created.
    Inside main(): Trying to use reflection to get another instance...
    Exception in thread "main" java.lang.reflect.InvocationTargetException
      at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
      at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
      at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
      at java.lang.reflect.Constructor.newInstance(Unknown Source)
      at TestSingleton.main(TestSingleton.java:13)
    Caused by: java.lang.IllegalStateException: Inside JavaSingleton(): JavaSingleton instance already created.
      at server.JavaSingleton.<init>(JavaSingleton.java:7)
      ... 5 more
    

    此示例适用于单例类(检查构造函数),但您仍可以为要阻止从其他类访问的私有方法实现此逻辑。

    在这种情况下,您还将声明一个静态实例并在私有方法中检查它的值,并在出现任何不需要的值时抛出错误。