Для экспериментального проекта я пытаюсь загрузить класс с URLClassLoader с включенным менеджером безопасности. Несмотря на то, что URL-адрес и класс существуют, код всегда завершается с ошибкой ClassNotFoundException.
Такое поведение наблюдалось, по крайней мере, со следующими версиями java (те, которые доступны в моей системе, MacOSX Mojave 10.14.6; еще не тестировались в Windows, Linux или других реализациях Java):
версия java "1.8.0_181" Java(TM) SE Runtime Environment (сборка 1.8.0_181-b13) Java HotSpot(TM) 64-битный сервер VM (сборка 25.181-b13, смешанный режим)
версия java "10.0.2" 17 июля 2018 г. Java(TM) SE Runtime Environment 18.3 (сборка 10.0.2+13) Java HotSpot(TM) 64-битный сервер VM 18.3 (сборка 10.0.2+13, смешанный режим)
версия java "12.0.1" 16.04.2019 Java(TM) SE Runtime Environment (сборка 12.0.1+12) Java HotSpot(TM) 64-битный сервер VM (сборка 12.0.1+12, смешанный режим, совместное использование)
После нескольких часов устранения неполадок (проверка путей, поиск невидимых символов в путях, чтение документации по загрузчикам классов и т. д.) я понял, что исключение возникает ТОЛЬКО при включенном диспетчере безопасности . Когда я отключаю его, класс находится без проблем. Я бы ожидал какого-то исключения безопасности, но не 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)