JAVA RMI

作者: const27 分类: All,开发-JAVA 发布时间: 2020-06-21 12:33

为什么要学RMI?最近复现weblogic洞的时候原理中提到了RMI,以及原来学习apache commons collections漏洞时接触到了RMI反序列化利用(当时没管),所以我觉得RMI如此常见,有学一学的必要

何为RMI

RMI,即远程方法调用,允许运行在一个JAVA虚拟机调用另一个JAVA虚拟机上的对象的方法.

RMI:实现一个远程接口

RMI的远程接口用于其他java虚拟机远程调用该接口下的对象的方法.

定义一个远程接口

import java.rmi.Remote;   //引入Remote接口
public interface IHello extends Remote{  //实现一个Remote接口
  public String sayHello(String name) throws java.rmi.RemoteException;
//若是Remote接口里某个方法抛出RemoteException异常,就意味着这是一个可以被远程调用的方法,但目前这个方法还未被具体实现.
}

远程接口的实现类

刚刚我们定义了一个Remote接口,但是他的方法还未具体实现.我们就来实现远程接口的实现类吧

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class HelloImpl extends UnicastRemoteObject implements IHello{ //定义一个远程接口实现类,注意这个远程实现类必须继承自UnicastRemoteObject,不然服务端不会发送该类的对象的存根(stub(下文讲))
    protected HelloImpl throws RemoteException{//必须要有一个抛出RemoteException异常的显示构造函数
        super();
    }
    public String sayHello(String name) throws RemoteException{
        return "Hello"+name;    //定义实现类方法的具体代码
    }
}

两台JVM通讯的第一步:RMI Registry

何为RMI Registry
可以理解为部署在被远程调用的JAVA虚拟机上的一个应用,用于将stub绑定到Registry服务的URL上。

服务端绑定stub到指定url

java.rmi.Naming.rebind("rmi://localhost:1099/hello", hello);   
//如此处就是把hello这个对象绑定在了rmi://localhost:1099/hello这个URL

随后客户端想要远程调用hello这个对象的方法时,就需

IHello hello = (IHello) Naming.lookup("rmi://localhost:1099/hello");
//此刻客户端查找出了hello对象的stub所在url,服务端返回hello对象的Stub
//此时的数据类型似乎必须是接口类

Stub和Skeleton

由上文可知当客户机远程调用一个对象时,返回的其实不是对象本身,而是stub.
你可以理解stub为一个中继站,当客户机调用该对象某个方法时,实际上是通过stub以socket的方式向服务器端的skeleton发送序列化处理的方法名和参数(skeleton可以理解为服务器端上的真实对象)
服务端的skeletion反序列化得到的方法名和参数并处理后再以socket的方式把该方法生成的结果传回stub,stub再把数据返回给客户机

代码实现远程调用

服务器端
import java.rmi.Remote;
import java.rmi.registry.*;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

interface Hello extends Remote{
    public String sayHello(String name) throws RemoteException;
}

class HelloImpl extends UnicastRemoteObject implements Hello{
    protected HelloImpl()throws RemoteException {
        super();
    }
    public String sayHello(String name) throws RemoteException{
        return "Hello"+name;
    }
}
------------------------------------------------------------------
public class Server{
    public static void main(String[] args){
    try{//必须有try..catch
        Hello hello = new HelloImpl();  //似乎数据类型只能是接口类
        LocateRegistry.createRegistry(1080);  //将RMI registry的端口设置为1080
        java.rmi.Naming.rebind("rmi://localhost:1080/hello", hello);//
将hello这个方法对象绑定在指定url上,rmi协议
        System.out.print("OK");
    }catch(Exception e){
        e.printStackTrace();
    }
}
}
客户机端
import java.rmi.Remote;
import java.rmi.registry.*;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.rmi.Naming;
public class Client{
    public static void main(String[] args){
        try{//必须有try..catch
        Hello hello_client = (Hello) Naming.lookup("rmi://localhost:1080/hello");  //服务器端绑定在该url的对象数据类型是Hello,所以这里的数据类型也是Hello
        System.out.print(hello_client.sayHello("fuck"));
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

结果

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

Leave a Reply

Your email address will not be published. Required fields are marked *

标签云