RMI 官方案例跑不起来?
在写《log4j2 漏洞模拟》时,用到了Java 的 RMI,中间踩到了一些坑,这篇文章给大家详细介绍下。
什么是RMI
RMI 是指 Remote Method Invocation 远程方法调用,通过本地端口对外暴露对象,供远程客户端调用,底层基于 TCP 协议实现。
与 RPC、Webservice 区别
都是分布式系统的调用的组件,三者都是基于 client-server 模型。
RPC, Remote Precedure Call,远程过程调用,为 Client 方屏蔽了底层细节,Client 并不知道调用的是本地还是远程。RPC 是一个更加泛的概念,可以这样理解非本地调用,都属于 RPC。
实际开发中常用的rpc 框架有:grpc、thrift、webservice。
RMI 是 RPC 在 OOP 中的一种实现,基于 tcp/ip 协议,并且只支持 Java 语言,无法跨语言调用。
而 webservice 基于 http 协议,支持跨语言调用,应用更加广泛。
官方案例的坑
官方案例直接拷贝过来:
package engine;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import compute.Compute;
import compute.Task;
public class ComputeEngine implements Compute {
public ComputeEngine() {
super();
}
public <T> T executeTask(Task<T> t) {
return t.execute();
}
public static void main(String[] args) {
if (System.getSecurityManager() == null) {
System.setSecurityManager(new SecurityManager());
}
try {
String name = "Compute";
Compute engine = new ComputeEngine();
// 将 engine 对象暴露出去,供远程client 使用
Compute stub =
(Compute) UnicastRemoteObject.exportObject(engine, 0);
Registry registry = LocateRegistry.getRegistry();
registry.rebind(name, stub);
System.out.println("ComputeEngine bound");
} catch (Exception e) {
System.err.println("ComputeEngine exception:");
e.printStackTrace();
}
}
}
暴露对象时,端口传的是0,由 RMI 或操作系统动态选择,不方便调试,我们改成一个自定义端口9999, 启动后报错:
查看端口是否启动:
找了很久问题出在这里:
这里获取 registry 不传参的话使用的是默认端口:1099 肯定连不上了。
ok 我们修改代码:
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
public class ComputeEngine implements Compute {
static {
System.out.println("执行了恶意代码");
}
public ComputeEngine() {
super();
}
public <T> T executeTask(Task<T> t) {
return t.execute();
}
public static void main(String[] args) {
if (System.getSecurityManager() == null) {
System.setSecurityManager(new SecurityManager());
}
try {
String name = "Compute";
Compute engine = new ComputeEngine();
Compute stub =
(Compute) UnicastRemoteObject.exportObject(engine, 9999);
Registry registry = LocateRegistry.getRegistry(9999);
registry.rebind(name, stub);
System.out.println("ComputeEngine bound");
} catch (Exception e) {
System.err.println("ComputeEngine exception:");
e.printStackTrace();
}
}
}
重新启动后,仍然报错:
这个问题比较隐秘,直接给出结论无论是否使用默认端口,都需要先显式创建 registry。
// 这就话是必须的!官方案例有错误
LocateRegistry.createRegistry(9999);
重新启动,成功!
另外权限问题需要提前解决,添加 jvm 参数:
-Djava.security.debug=access,failure -Djava.security.manager -Djava.security.policy=E:\IdeaProjects\Test\src\main\resources\my.policy
这个参数 -Djava.security.debug=access,failure 能够调试你的权限,很重要!
my.policy 文件:
grant {
permission java.security.AllPermission;
};
按照以上步骤操作一定能跑起来,亲测有效!!
如果觉得还不错的话,关注、分享、在看, 原创不易,且看且珍惜~