ClassNotFoundException, выдаваемое URLClassLoader при включенном диспетчере безопасности.

Для экспериментального проекта я пытаюсь загрузить класс с 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)

person Corentor    schedule 09.08.2019    source источник


Ответы (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. Хотя его ответ был очень полезным, я все еще думаю, что есть ошибка в том смысле, что SDK (а не приложение) создает неправильное исключение: ClassNotFoundException вместо разрешения или исключения безопасности.

Людей, не знающих о свойстве -Djava.security.debug=access (а также пользователей или администраторов в рабочем контексте), это может ввести в заблуждение и сбить с толку.

В любом случае, моя проблема закрыта, и я надеюсь, что это решение будет полезно другим людям, столкнувшимся с похожими проблемами.

С уважением ко всем.

person Corentor    schedule 15.08.2019