语境
这是一个使用 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 useCodebaseOnlytruejava.rmi.server.codebaseWorkRequestSquareuseCodebaseOnlytrue
当在服务器上useCodebaseOnly设置为false,并且WorkRequestSquare从服务器 CLASSPATH 中删除类时,服务器现在从 HTTP 服务器获取类定义。这是一个有效的绕过。
问题
由于客户端和服务器具有相同的codebase属性值,因此仍然存在异常。
何时useCodebaseOnly默认为true服务器 JVM 应该忽略codebase添加到 RMI 流中的客户端,但无论如何它应该使用自己的codebase来检索WorkRequestSquare类。
无论如何,这应该是成功的,因为客户端和服务器codebase值是相同的。
有人可以对这件事有所了解吗?