启用安全管理器时,URLClassLoader引发ClassNotFoundException

3
为了进行一个实验性项目,我试图使用URLClassLoader加载一个启用了安全管理器的类。尽管URL和类确实存在,但代码总是失败并出现ClassNotFoundException。
这种行为至少在以下Java版本中观察到(我的系统上可用的版本,MacOSX Mojave 10.14.6; 尚未在Windows或Linux上或使用其他Java实现进行测试):
java version "1.8.0_181" Java(TM) SE Runtime Environment (build 1.8.0_181-b13) Java HotSpot(TM) 64-Bit Server VM (build 25.181-b13, mixed mode)
java version "10.0.2" 2018-07-17 Java(TM) SE Runtime Environment 18.3 (build 10.0.2+13) Java HotSpot(TM) 64-Bit Server VM 18.3 (build 10.0.2+13, mixed mode)
java version "12.0.1" 2019-04-16 Java(TM) SE Runtime Environment (build 12.0.1+12) Java HotSpot(TM) 64-Bit Server VM (build 12.0.1+12, mixed mode, sharing)
经过数小时的故障排除(检查路径,查找路径中的不可见字符,阅读类加载器文档等),我意识到仅当启用安全管理器时才会出现异常。当我禁用它时,类可以被正常找到。我本来期望会出现某种安全异常,但没有预料到会出现ClassNotFoundException。
显然,缺乏安全管理器严重限制了类加载器的实用性(特别是如果想要加载不受信任的已签名的类和JAR文件)。创建类加载器的权限在安全策略中显然被授予。
下面的代码是重新产生问题所需的最小代码。
我对Java相对较新。我可能在做一些愚蠢的事情。有人以前观察到过这种行为吗?这是故意的吗?我有什么遗漏吗?
感谢任何帮助。
.
├── ClassLoaderTest.class
├── ClassLoaderTest.java
├── Makefile
├── security.policy
└── untrusted
    ├── Makefile
    ├── Untrusted.class
    └── Untrusted.java

1 directory, 7 files

// ClassLoaderTest.java

import java.net.URLClassLoader;
import java.net.URL;

public class ClassLoaderTest {

    public static void main(String[] args) throws Exception {
        URL[] urls = new URL[1];
        urls[0] = new URL("file:./untrusted/"); // untrusted/ could be anywhere; for the purpose of the test, it is in the working directory
        URLClassLoader loader = new URLClassLoader(urls);
        Class<?> cl = loader.loadClass("Untrusted"); // one tries to load: untrusted/Untrusted.class
        cl.getConstructor().newInstance(); // or cl.getDeclaredConstructor().newInstance()
        System.exit(0);
    }
}

public class Untrusted {
    public Untrusted() {
        System.out.println("Instantiation of Untrusted");
    }

    public static void main(String args[]) {
        Untrusted u = new Untrusted();
    }
}

// security.policy
// permissions granted to any code (no matter where it came from or whether it is signed)
// THIS IS OBVIOUSLY FOR TESTING ONLY, only trusted sources should be granted a createClassLoader permission
grant {
  permission java.lang.RuntimePermission "createClassLoader";
};

以下是程序的结果:

没有安全管理器:正常工作。

$ java ClassLoaderTest
Instantiation of Untrusted

启用安全管理员后:失败!

$ java -Djava.security.manager -Djava.security.policy=security.policy ClassLoaderTest
Exception in thread "main" java.lang.ClassNotFoundException: Untrusted
    at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:436)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:588)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
    at ClassLoaderTest.main(ClassLoaderTest.java:10)
1个回答

1

提交问题给Oracle后,他们通过以下非常有用的标志(适用于与安全管理器一起工作的人)解决了问题:-Djava.security.debug = access。

https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8229532

问题就变得明确了:
// ... more lines above (skipped for clarity)
access: access denied ("java.util.PropertyPermission" "user.dir" "read") // shows up only in debug mode
Exception in thread "main" java.lang.ClassNotFoundException: Untrusted
...

因此,必须在security.policy文件中添加一个权限:

grant {
  permission java.lang.RuntimePermission "createClassLoader";
  permission java.util.PropertyPermission "user.dir", "read"; // new line
};

而问题被 Oracle 的指派人标记为不存在,并已关闭。虽然他的回复非常有帮助,但我仍认为存在一个 bug,即 SDK 抛出了错误的异常(不是应用程序):ClassNotFoundException 而不是 Permission 或 Security Exception。

对于不了解 -Djava.security.debug=access 属性的人(以及生产环境中的用户或管理员),这可能会导致误导和混淆。

无论如何,我的问题已经关闭,我希望这个解决方案能够帮助其他遇到类似问题的人。

祝大家一切顺利。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接