Учебное пособие по Java RMI - AccessControlException: доступ запрещен (java.io.FilePermission

Вчера я попытался начать работу с Java RMI. Я нашел этот учебник по солнцу (http://java.sun.com/docs/books/tutorial/rmi/index.html) и начал с реализации сервера. Но каждый раз, когда я запускаю программу (работает реестр rmire), я получаю исключение AccessControlException со следующей трассировкой StackTrace:

LoginImpl exception:
java.security.AccessControlException: access denied (java.io.FilePermission \\\C\ProjX\server\serverProj\bin\usermanager read)
    at java.security.AccessControlContext.checkPermission(AccessControlContext.java:264)
    at java.security.AccessController.checkPermission(AccessController.java:427)
    at java.lang.SecurityManager.checkPermission(SecurityManager.java:532)
    at java.lang.SecurityManager.checkRead(SecurityManager.java:871)
    at java.io.File.exists(File.java:700)
    at sun.net.www.protocol.file.Handler.openConnection(Handler.java:80)
    at sun.net.www.protocol.file.Handler.openConnection(Handler.java:55)
    at java.net.URL.openConnection(URL.java:943)
    at sun.rmi.server.LoaderHandler.addPermissionsForURLs(LoaderHandler.java:1020)
    at sun.rmi.server.LoaderHandler.access$300(LoaderHandler.java:52)
    at sun.rmi.server.LoaderHandler$Loader.<init>(LoaderHandler.java:1108)
    at sun.rmi.server.LoaderHandler$Loader.<init>(LoaderHandler.java:1089)
    at sun.rmi.server.LoaderHandler$1.run(LoaderHandler.java:861)
    at java.security.AccessController.doPrivileged(Native Method)
    at sun.rmi.server.LoaderHandler.lookupLoader(LoaderHandler.java:858)
    at sun.rmi.server.LoaderHandler.loadProxyClass(LoaderHandler.java:541)
    at java.rmi.server.RMIClassLoader$2.loadProxyClass(RMIClassLoader.java:628)
    at java.rmi.server.RMIClassLoader.loadProxyClass(RMIClassLoader.java:294)
    at sun.rmi.server.MarshalInputStream.resolveProxyClass(MarshalInputStream.java:238)
    at java.io.ObjectInputStream.readProxyDesc(ObjectInputStream.java:1494)
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1457)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1693)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1299)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:339)
    at sun.rmi.registry.RegistryImpl_Skel.dispatch(Unknown Source)
    at sun.rmi.server.UnicastServerRef.oldDispatch(UnicastServerRef.java:375)
    at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:240)
    at sun.rmi.transport.Transport$1.run(Transport.java:153)
    at java.security.AccessController.doPrivileged(Native Method)
    at sun.rmi.transport.Transport.serviceCall(Transport.java:149)
    at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:460)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:701)
    at java.lang.Thread.run(Thread.java:595)
    at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(Unknown Source)
    at sun.rmi.transport.StreamRemoteCall.executeCall(Unknown Source)
    at sun.rmi.server.UnicastRef.invoke(Unknown Source)
    at sun.rmi.registry.RegistryImpl_Stub.rebind(Unknown Source)
    at startserver.StartServer.main(StartServer.java:22)

Мой файл server.policy выглядит так:

grant {
    permission java.security.AllPermission;
};

Но я тоже пробовала этот...

grant {
    permission java.security.AllPermission;
    permission java.io.FilePermission "file://C:/ProjX/server/serverProj/bin/usermanager", "read";
};

... и этот (и несколько других :-():

grant codeBase "file:///-" {
    permission java.security.AllPermission;
};

Но во всех случаях результат один. И да, файл политики находится в пути (я вижу исключение Parse Exception, когда пишу неправильные статусы в файл политики). Я попробовал несколько других созвездий "/" и "\", но это не дало никакого эффекта.

Я использую Eclipse, и мои параметры VM выглядят так:

-cp C:\ProjX\server\serverProj\bin\usermanager\
-Djava.rmi.server.codebase=file://C:/ProjX/server/serverProj/bin/usermanager/
-Djava.rmi.server.hostname=XYZ (anonymized)
-Djava.security.policy=server.policy

Скомпилированные классы Remote-Interface и класс реализации интерфейса (LoginImpl) находятся по этому пути: "C:/ProjX/server/serverProj/bin/usermanager/". Основной метод, в котором я создаю экземпляр и повторно привязываю заглушку к реестру, находится в другом пакете и выглядит следующим образом:

public static void main(String[] args) {
    if (System.getSecurityManager() == null) {
        System.setSecurityManager(new SecurityManager());
    }
    try {
        String name = "Login";
        Login login = new LoginImpl();
        Login stub = (Login) UnicastRemoteObject.exportObject(login, 0);
        Registry registry = LocateRegistry.getRegistry();
        registry.rebind(name, stub);
        System.out.println("LoginImpl bound");
    } catch (Exception e) {
        System.err.println("LoginImpl exception:");
        e.printStackTrace();
    }
}

У кого-нибудь есть совет для меня? Спасибо за помощь.


Таким образом, вопрос тот же (java.rmi.UnmarshalException показывает, что изменение кодовой базы не является решением моего AccessControlException). И нет: я не хочу покупать плагин "GB" ;-).


person user25913    schedule 07.10.2008    source источник
comment
У меня была аналогичная проблема. Я решил это с помощью этого подхода - stackoverflow.com/a/44275905/1509058   -  person Devendra Singh    schedule 31.05.2017


Ответы (5)


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

Вернемся к вопросу...

-Djava.rmi.server.codebase=file://C:/ProjX/server/serverProj/bin/usermanager/

Это должно быть либо "file:///C:/...", либо "file:/C:/...". Подумайте о http. "http://C:/..." относится к хосту с именем C. Обратите внимание, что в сообщении об исключении отсутствует двоеточие, потому что это просто синтаксис для номера порта.

Причина, по которой вы получаете исключение безопасности, даже если вы предоставляете разрешения для всего кода, заключается в том, что RMI ограничивает разрешения соответствующим образом с учетом задействованных URL-адресов (используя форму двух аргументов AccessController doPrivileged).

person Tom Hawtin - tackline    schedule 07.10.2008
comment
Привет! Я нашел это очень полезным. Не могли бы вы сказать мне, где хранить текстовый файл server.policy? - person user1799214; 14.02.2016
comment
@user1799214 user1799214 Оригинальный постер использовал текущий рабочий каталог сервера hte, указав с помощью -Djava.security.policy=server.policy. (Как всегда, безопасно снять ограничения на код, доступный по сети, сложно.) - person Tom Hawtin - tackline; 14.02.2016

Хорошо, у меня есть. Это не свойство rmiregistry (работает без каких-либо параметров). В моей кодовой базе VM-Parameter было две ошибки:

-cp C:\ProjX\server\serverProj\bin\usermanager\
-Djava.rmi.server.codebase=file://C:/ProjX/server/serverProj/bin/usermanager/
-Djava.rmi.server.hostname=XYZ (anonymized)
-Djava.security.policy=server.policy

... вместо этого должно выглядеть так:

-Djava.rmi.server.codebase=file:/C:/ProjX/server/serverProj/bin/
-Djava.rmi.server.hostname=XYZ (anonymized)
-Djava.security.policy=server.policy

=> file:/ (только одна косая черта) + неправильное окончание пакета.

Но трассировка была настолько запутанной, что моей первой мыслью было, что что-то не так с файлом политики или конфигурацией политики.

Тем не менее: Спасибо за помощь и счастливого взлома. ;-)

person user25913    schedule 14.10.2008

Вы также можете программно установить свойство java.rmi.server.codebase:

Hello h = null;
Properties props = System.getProperties();
System.setProperty("java.rmi.server.codebase", "file:/C:/PROJECTX/bin/");
try {
  h = new HelloImpl();
  Naming.bind("//localhost:1099/HelloService", h);
  System.out.println("Serwis gotów...");
} catch (RemoteException e) {
  e.printStackTrace();
} catch (MalformedURLException e) {
  e.printStackTrace();
} catch (AlreadyBoundException e) {
  e.printStackTrace();
}

для некоторой гипотетической службы Hello RMI.

person Piotr Kochański    schedule 12.11.2008
comment
Кажется, я не могу установить это свойство. Моя Java, похоже, не позволяет мне выполнять операцию set. - person Frederick Nord; 11.03.2017

Я думаю, что исключение на самом деле исходит из rmiregistry. Эта часть трассировки стека заставляет меня так думать. Заглушка для rmiregistry получает исключение и передает его обратно в результате попытки повторной привязки.

    at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(Unknown Source)
    at sun.rmi.transport.StreamRemoteCall.executeCall(Unknown Source)
    at sun.rmi.server.UnicastRef.invoke(Unknown Source)
    at sun.rmi.registry.RegistryImpl_Stub.rebind(Unknown Source)

Попробуйте запустить rmiregistry с -J-Djava.security.policy=all.policy, где файл политики предоставляет все разрешения (по крайней мере, чтобы все заработало).

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

person bhavanki    schedule 14.10.2008

Он просто отлично работает, когда я исправил переменную CLASSPATH перед запуском реестра rmi. Я думаю, что идея в том, что реестр RMI будет загружать ваши удаленные заглушки, и у него должен быть доступ. Это было легко, поместив мои классы в CLASSPATH перед запуском реестра. Таким образом, это не связано с какой-либо другой причиной, такой как JDK 7 или файл:/протокол.

person Tarek    schedule 22.08.2013