Java RMI教程 - AccessControlException:访问被拒绝(java.io.FilePermission)

15

昨天我尝试着开始学习Java RMI。 我找到了这个sun教程(http://java.sun.com/docs/books/tutorial/rmi/index.html) 并从服务器实现开始。 但是每次我启动程序(rmiregistry正在运行),我都会得到一个具有以下堆栈跟踪的AccessControlException:

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;
};

但在每种情况下结果都是相同的。是的,在路径中存在策略文件(当我将错误语句写入策略文件时,我看到了一个解析异常)。我尝试了几个其他的"/"和""组合,但没有效果。

我使用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问题的方法)。不,我不想购买“GB”插件。


我曾经遇到过类似的问题。我通过这种方法解决了它 - https://dev59.com/JZTfa4cB1Zd3GeqPLhXg#44275905 - Devendra Singh
5个回答

7

授予所有代码的所有权限是非常糟糕的。任何RMI客户端都可以像已登录的用户一样随意操作。通常情况下,尽可能限制权限,特别是当您不知道代码来自何处时。

回到问题本身...

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

这应该是 "file:///C:/...""file:/C:/..."。可以把它想成 http。 "http://C:/..." 指的是名为 C 的主机。请注意,异常消息省略了冒号,因为那只是端口号的语法。
即使您授予所有代码权限,您仍会收到安全异常的原因是 RMI 根据涉及的 URL 限制权限(使用 AccessController doPrivileged 两个参数形式)。

嗨!我发现这非常有帮助。你能告诉我在哪里存储server.policy文本文件吗? - user1799214
@user1799214 原帖中使用了服务器的当前工作目录,并通过 -Djava.security.policy=server.policy 进行了指定。(一如既往,安全地移除网络可访问代码的限制是非常棘手的。) - Tom Hawtin - tackline

7

好的,我明白了。这并不是rmiregistry属性(可以不带任何参数运行)。我的代码库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

应该像这样:

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

=> file:/(只有一个斜杠)+错误的包结尾。

但是跟踪信息非常混乱,我的第一反应是,策略文件或策略配置可能有问题。

无论如何:感谢您的帮助,祝愉快的黑客活动。 ;-)


2
您可以通过编程来设置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服务。


我似乎无法设置这个属性。我的Java好像不允许我执行设置操作。 - Frederick Nord

0

在启动rmi注册表之前修复CLASSPATH变量后,它就可以正常工作。我认为这个想法是RMI注册表将加载您的远程存根,并且应该具有访问权限。通过在运行注册表之前将我的类放在CLASSPATH上,这很容易实现。因此,它与任何其他原因(如JDK 7或file:/协议)无关。


0

我认为异常实际上是来自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)

尝试使用-J-Djava.security.policy=all.policy运行rmiregistry,其中策略文件授予所有权限(至少可以让事情开始)。

最终,您可能还希望切换到HTTP代码库URL,以便可以在与服务器不同的计算机上运行客户端。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接