Разрешение отказало ожиданию при получении системного свойства os.arch при включенном диспетчере безопасности Java

У меня есть простое java-приложение ClientApp, в котором включен Java Security Manager. Это приложение пытается вызвать метод Test jar, который извлекает системное свойство «os.arch». Поскольку это отнимает много времени, мы вызываем новый поток, используя задачу Completable.

Это дает исключение

java.util.concurrent.ExecutionException: java.security.AccessControlException: access denied ("java.util.PropertyPermission" "os.arch" "read")
    at java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:357)
    at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1895)
    at SecurityApplication.ClientApp.main(ClientApp.java:23)
Caused by: java.security.AccessControlException: access denied ("java.util.PropertyPermission" "os.arch" "read")
    at java.security.AccessControlContext.checkPermission(AccessControlContext.java:472)
    at java.security.AccessController.checkPermission(AccessController.java:884)
    at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
    at java.lang.SecurityManager.checkPropertyAccess(SecurityManager.java:1294)
    at java.lang.System.getProperty(System.java:717)
    at com.ravindra.CustomSupplier.get(CustomSupplier.java:10)
    at com.ravindra.CustomSupplier.get(CustomSupplier.java:5)
    at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1590)
    at java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1582)
    at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
    at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
    at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
    at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)

Клиентское приложение:

import com.test.App;

public class ClientApp
{
    public static void main(String[] args)
    {
        //Enable security 
        SecurityManager securityManager = new SecurityManager();
        System.setSecurityManager(securityManager);

        TestApp app = new TestApp();

        Future<String> future = app.getOsArchitecture();

        try
        {
            // blocking Aysnc get Future call for result
            System.out.println(future.get());
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}

Test.jar имеет файл класса ниже 2

Тест.java

final public class TestApp
{
    public Future<String> getOsArchitecture()
    {
        CompletableFuture<String> completableFuture
                = CompletableFuture.supplyAsync(new CustomSupplier());

        return completableFuture;
    }
}

CustomSupplier.java

public class CustomSupplier implements Supplier<String> {
    public CustomSupplier() {
    }

    public String get() {
        //Time consuming complex Task and finally return "os.arch"
        //...
        //...
        //...
        return System.getProperty("os.arch");
    }
}

Однако, когда мы отключаем Java Security Manager, программа работает правильно.

Любая помощь приветствуется.


comment
System.getProperty(String) выполняет поиск по одному хешу. Это точно не отнимет много времени. Даже если бы это было так, выполнение действия асинхронно, но ожидание результата сразу после отправки, только немного увеличивало требуемое время.   -  person Holger    schedule 25.02.2020
comment
Задача, требующая времени, означает бизнес-логику, связанную с моим проектом.   -  person Pankaj007    schedule 25.02.2020
comment
Итак, вы выполняете трудоемкую асинхронную задачу, которая не зависит от значения System.getProperty("os.arch"), и в конце вы возвращаете в качестве результата значение System.getProperty("os.arch"), которое не имеет никакого отношения к тому, что сделала асинхронная задача?   -  person Holger    schedule 25.02.2020
comment
да, вы правы   -  person Pankaj007    schedule 27.02.2020
comment
Привет всем, мы решили проблему. API был написан таким образом, что нам нужно было передать собственный Executor Service, чтобы он не использовал общий пул ForkJoin для создания потока. Когда мы создаем нашу собственную службу Executor, она использует контекст приложения, поэтому у нее есть разрешение на получение свойства System.   -  person Pankaj007    schedule 27.02.2020


Ответы (1)


По умолчанию ForkJoinPool использует потоки, настроенные на отсутствие привилегий.

Одним из решений является создание нового экземпляра ForkJoinPool с пользовательской фабрикой потоков или любой другой реализацией Executor с использованием обычных потоков. Загвоздка в том, что для создания собственного пула потоков требуются соответствующие разрешения, предоставленные менеджером безопасности.

Альтернативой является выполнение запроса как привилегированного действия, игнорирующего ограничения вызывающего потока:

public class CustomSupplier implements Supplier<String> {
    public CustomSupplier() {
    }

    public String get() {
        //Time consuming complex Task and finally return "os.arch"
        //...
        //...
        //...
        return AccessController.doPrivileged(
            (PrivilegedAction<String>)() -> System.getProperty("os.arch"));
    }
}

Это игнорирует привилегии вызывающей стороны и выполняет запрос с разрешения кода, вызывающего doPrivileged, то есть вашего CustomSupplier. По умолчанию коду приложения предоставляется право на чтение системного свойства "os.arch".

Но поскольку ожидается, что это системное свойство никогда не изменится в течение жизненного цикла приложения, а запрос к нему не является дорогостоящей операцией, нет необходимости выполнять запрос в фоновом режиме.

public class CustomSupplier implements Supplier<String> {
    public CustomSupplier() {
    }

    public String get() {
        //Time consuming complex Task and finally return "os.arch"
        //...
        //...
        //...
        return ARCH;
    }
    static final String ARCH = System.getProperty("os.arch");
}
person Holger    schedule 27.02.2020
comment
постараюсь и дам вам знать. Спасибо. - person Pankaj007; 02.03.2020