语境
这是一个使用 Java RMI 的应用程序,具有:
- 主机 A 上的客户端和主机 B 上的服务器。
- 主机 A 上的 HTTP 服务器,它保存客户端和服务器使用的所有类的副本。
- RMI 注册表是从服务器代码创建的,它共享服务器 JVM 和 CLASSPATH。
对于服务器和客户端应用程序:
- 该
java.rmi.server.codebase
属性(在代码中)设置为 HTTP 服务器 URL。 - 具有适当策略的安全经理已就位。
java.rmi.server.hostname
设置为 LAN 地址。
方法调用:
- 服务器方法是使用类型参数
WorkRequest
(抽象类)定义的。 - 客户端使用子类调用此方法
WorkRequestSquare
。 WorkRequestSquare
从未在服务器代码中提及
服务器代码
Object execute(WorkRequest work) throws RemoteException { return work.execute(); }
客户端代码:
try { servant.execute(new WorkRequestSquare(123)); }
catch (RemoteException e) {
System.out.println("Error while submitting work request:");
e.printStackTrace();
}
当服务器在 CLASSPATH 中包含所有相关类并且不需要动态加载时,此代码有效。
问题
从服务器端的 CLASSPATH 中删除WorkRequestSquare
会在客户端抛出异常(服务器端不会抛出任何东西):
Error while submitting work request:
java.rmi.ServerException: RemoteException occurred in server thread; nested exception is:
java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is:
java.lang.ClassNotFoundException: WorkRequestSquare
at sun.rmi.server.UnicastServerRef.dispatch(Unknown Source)
...
WorkRequestSquare
从未在服务器代码中明确提及,它应该是可动态加载的,但这不会发生。
旁路
Stuart Marks 让我知道该属性java.rmi.server.useCodebaseOnly
应设置为false
.
在 JDK 7u21 中,此属性的默认值已从 更改false
为。设置为防止 JVM 从任何位置动态加载类,除了使用. 这意味着客户端 JVM 在编组参数时设置的代码库值被.true
useCodebaseOnly
true
java.rmi.server.codebase
WorkRequestSquare
useCodebaseOnly
true
当在服务器上useCodebaseOnly
设置为false
,并且WorkRequestSquare
从服务器 CLASSPATH 中删除类时,服务器现在从 HTTP 服务器获取类定义。这是一个有效的绕过。
问题
由于客户端和服务器具有相同的codebase
属性值,因此仍然存在异常。
何时useCodebaseOnly
默认为true
服务器 JVM 应该忽略codebase
添加到 RMI 流中的客户端,但无论如何它应该使用自己的codebase
来检索WorkRequestSquare
类。
无论如何,这应该是成功的,因为客户端和服务器codebase
值是相同的。
有人可以对这件事有所了解吗?