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

按照以上步骤操作一定能跑起来,亲测有效!!


如果觉得还不错的话,关注、分享、在看, 原创不易,且看且珍惜~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

【非典型Coder】

赏个鸡腿吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值