带有 SecurityManager 的 Constructor.newInstance 与 Class.newInstance

Posted

技术标签:

【中文标题】带有 SecurityManager 的 Constructor.newInstance 与 Class.newInstance【英文标题】:Constructor.newInstance vs Class.newInstance with a SecurityManager 【发布时间】:2011-02-05 19:08:02 【问题描述】:

在 Java 中,当存在拒绝访问检查抑制的 SecurityManager 时,Constructor 的 newInstance 方法会起作用,而 Class 的 newInstance 会抛出 SecurityException。这是一个例子:

import java.lang.reflect.ReflectPermission;
import java.security.Permission;

public class Test 
    public static void main(String[] args) throws Exception 
        System.setSecurityManager(new SecurityManager() 
            @Override
            public void checkPermission(Permission perm) 
                if (perm instanceof ReflectPermission && "suppressAccessChecks".equals(perm.getName())) 
                    throw new SecurityException();
                
            
        );

        String.class.getConstructor().newInstance(); // works
        String.class.newInstance(); // throws SecurityException
    

运行它会产生:

Exception in thread "main" java.lang.SecurityException
    at Test$1.checkPermission(Test.java:10)
    at java.lang.reflect.AccessibleObject.setAccessible(AccessibleObject.java:125)
    at java.lang.Class$1.run(Class.java:351)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.lang.Class.newInstance0(Class.java:348)
    at java.lang.Class.newInstance(Class.java:325)
    at Test.main(Test.java:16)

Class.newInstance 的 JavaDoc 说它在 SecurityManager 上调用 checkMemberAccess 和 checkPackageAccess,但我不知道为什么它会调用 setAccessible。这种行为差异是否有原因?

我正在使用:

java version "1.6.0_20"
OpenJDK Runtime Environment (IcedTea6 1.9.5) (ArchLinux-6.b20_1.9.5-1-x86_64)
OpenJDK 64-Bit Server VM (build 17.0-b16, mixed mode)

【问题讨论】:

您是否尝试过阅读代码/cmets?这可能表明它为什么会这样做。 我刚刚查看了 OpenJDK 的 Class.java,在 setAccessible 调用之前不久发现了这条评论:“禁用构造函数的可访问性检查,因为无论如何我们都必须在这里进行安全检查(堆栈深度是错误的构造函数的安全检查工作)”。我仍然不确定为什么应该与外界有所不同。 评论解释得很好,但是对我来说它看起来像一个错误:这个异常可以被简单地忽略,当 setAccessible 失败时,要么它已经可以访问并且 newInstance 将工作,或者它不是并且newInstance 稍后会抛出。两者都是正确的,所以忽略异常就可以了。 【参考方案1】:

Class.newInstance() 调用SecutrityManager.checkMemberAccess(this, Member.PUBLIC),默认情况下,它授予所有公共成员的访问权限。 checkPermission() 被调用(由checkMemberAccess())仅当相关成员公开时。

因此,您对checkPermission() 的覆盖不会影响对公共成员的访问。您需要覆盖checkMemberAccess()

这里是来自Class的Javadocs的相关引用:

(newInstance() failed if) 调用 s.checkMemberAccess(this, Member.PUBLIC) 拒绝创建此类的新实例

SecurityManager:

默认策略(checkMemberAccess())是允许访问 PUBLIC 成员,以及访问与调用者具有相同类加载器的类。在所有其他情况下,此方法调用 checkPermission() ...

【讨论】:

恐怕你没抓住重点。 OP 仅执行相当于 new String() 的操作,它是公开的,实际上应该总是成功,但考虑到他的安全设置,Class.newInstance() 失败。

以上是关于带有 SecurityManager 的 Constructor.newInstance 与 Class.newInstance的主要内容,如果未能解决你的问题,请参考以下文章

Localhost、Php artisan serve 和 homestead Pro/Cons

[Shiro] tutorial 1 :SecurityManager and Subject

自定义 SecurityManager 中的 ClassCircularityError

Shiro遇到的SecurityManager红色警告

Java SecurityManager 多线程

SecurityManager.IsGranted() 行为