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

Вчера я попытался начать работу с Java RMI. Я нашел этот учебник по солнцу (http://java.sun.com/docs/books/tutorial/rmi/index.html) и начал с реализации сервера. Но каждый раз, когда я запускаю pogram (работает rmiregistry), я получаю 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

Скомпилированные классы удаленного интерфейса и класса интерфейса (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). И нет: я не хочу покупать плагин "G B"; -).

Ответы

Ответ 1

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

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

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

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

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

Ответ 2

Хорошо, у меня есть. Это не было и острейшее свойство 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

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

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

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

Ответ 3

Вы также можете установить программно свойство 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();
}

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

Ответ 4

Я думаю, что исключение действительно выходит из 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, чтобы вы могли запускать клиентов на машине отдельно от вашего сервера.

Ответ 5

У меня очень короткий вопрос...

Почему он использует этот путь: "file://C:/ProjX/server/serverProj/bin/usermanager" я думаю, что он в окнах, а пути в окнах написаны C:\ProjX...... Я спрашиваю, потому что у меня есть некоторые проблемы с RMI, но у меня есть файл политики таким образом:

grant codebase     
"file:///C:\Users\anna\Desktop\lab5\Eclipse\ProgramareServer\programare.jar-" { 
    permission java.security.AllPermission;
};

Неверно?

Ответ 6

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