第十章远程调用和组件安全远程调用和组件,给程序功能的扩充提供了较大的支持。本章主要针对目前比较流行的远程调用方法和常见的组件进行安全讲解。远程调用(RPC/RMI)为程序的分布式应用开发架构提供了技术支持,它不需要了解底层网络通信协议,在应用层通过网络从远程计算机程序上请求服务。本章首先讲解远程调用的基本原理和安全问题。ActiveX是微软技术系列中提供的一种控件开发模型,将组件或对象打包,提高了程序的重用性;JavaApplet是采用Java创建的基于HTML的程序,浏览器将其暂时下载到用户的硬盘上,并在Web页打开时在本地运行。本章对这两种技术也进行了安全方面的讲解。DCOM是在微软技术系列中,以RPC为基础思想建立的组件模型,能让组件以可靠、安全和高效的方式进行网络通讯;EJB是SUN系列中以RMI为基础思想建立的服务器端组件模型,也能部署分布式应用程序,并能充分利用java跨平台的优势。本章对这两种技术进行了安全方面的阐述。CORBA由OMG组织制订,是OMG为解决分布式处理环境中,不同平台、不同语言甚至不同硬件系统之间的通讯而提出的一种解决方案。本章最后对CORBA安全进行了讲解。10.1远程调用安全10.1.1远程调用概述传统的网络分布式程序需要进行复杂的底层通信编程,但是有了远程过程调用(RemoteProcedureCall,RPC)之后,开发网络分布式应用程序更加容易了。RPC的出现,让开发者不需要了解底层网络通信协议,直接通过网络从远程计算机程序上请求服务。该技术在1981年由B.J.Nelson在其博士论文中提出,后被开放式软件基金会(OSF)制定为分布式计算环境(DCE)的分布式计算标准。RPC通信模型是基于客户/服务器通信模型的,是一种同步通信方式,即:调用方必须等待服务器响应。在客户端,RPC为远程过程提供了抽象,在调用时,其底层消息传递机制对客户来说都是透明的。在Java系列中,RMI(RemoteMethodInvocation)技术是远程过程调用的一种实现。RMI使用Java远程消息交换协议(JavaRemoteMessagingProtocol,JRMP)进行通信。用JavaRMI开发的应用系统可以部署在任何支持Java运行环境的平台上。如图是RPC/RMI通信过程:从图中可以看出,在RPC中,服务以过程的形式放在服务器端,客户负责请求服务,服务器执行客户的请求,运行被调用的过程。RPC在整个调用过程中需要经过的步骤如下:1:客户端请求进行远程调用,激活客户端存根,指定目标服务器;2:客户端存根将被调用的过程和参数打包,作为消息发送给服务器,等待数据消息的返回;3:服务器接收消息,服务器存根根据消息中的过程和参数等信息,调用服务器端的过程;4:服务器将结果作为消息返回给客户端存根;5:客户端存根将结果返回给用户。什么情况下适合使用远程调用呢?举一个例子,某公司内部办公系统的结构如下:客户端使用桌面应用程序。很显然,为了应对数据库的迁移或改变,访问数据库的代码不应该写在客户端,否则会造成大量客户端的改变。此时,访问数据库的代码写在服务器端,作为一个方法或过程的形式对外发布,客户端可在不知道服务器细节,也不知道底层通信协议的基础上,访问服务器端的这些方法,就好像调用自己机器上的方法一样。如果用Java实现,就可以使用RMI技术。当然,RMI还有很多其它的功能,读者可以参考相应文献。以上面应用为例,服务器端访问数据库(如查询)的代码模拟如10_01_Query.java。importjava.rmi.RemoteException;importjava.rmi.server.UnicastRemoteObject;publicclassP10_01_QueryextendsUnicastRemoteObjectimplementsP10_01_QueryInterface{publicP10_01_Query()throwsRemoteException{}publicStringquery()throwsRemoteException{//查询数据库代码return查询结果;}}importjava.rmi.Remote;importjava.rmi.RemoteException;publicinterfaceP10_01_QueryInterfaceextendsRemote{publicStringquery()throwsRemoteException;}P10_01_QueryInterface.java的代码为:很显然,服务器端的P10_01_QueryInterface接口内并没有核心代码。接下来将服务器对象对外发布。见代码P10_01_RunServer.java。importjava.rmi.Naming;publicclassP10_01_RunServer{publicstaticvoidmain(String[]args)throwsException{P10_01_QueryInterfacequeryInterface=newP10_01_Query();//启动注册表Runtime.getRuntime().exec(rmiregistry);//将这个对象起一个JNDI名称,放入注册表Naming.rebind(queryInterface,queryInterface);}}运行,服务器端的对象即对外发布。客户端得到服务器端发布的接口,然后远程调用服务器端的方法,客户端代码见P10_01_Client.java:importjava.rmi.Naming;publicclassP10_01_Client{publicstaticvoidmain(String[]args)throwsException{P10_01_QueryInterfacequeryInterface=(P10_01_QueryInterface)Naming.lookup(rmi://127.0.0.1/queryInterface);Stringresult=queryInterface.query();System.out.println(result);}}运行,即可调用服务器端的query方法。显示结果为:从上面的例子可以看出,客户端无需知道服务器端的核心代码,只需要知道接口即可。当然,在该例子中,省略了底层的一些通信细节的支持类。以上例子同时说明体现了RPC的其他好处:1:给程序在异构环境下进行通信提供了可能(异构主要可以体现在:网络环境中的多种硬件系统平台;硬件平台上的不同的系统软件;不同的网络协议或网络体系结构连接;等等。2:在异构网络环境下,需要把分散在各地的计算机系统集成起来,充分利用系统中分散的计算资源,由网络中的多台计算机协同工作完成某一任务,RPC也给这种需求的实现提供了可能。例一:HelloWordHelloTask接口:publicinterfaceHelloTask{publicStringexecute();}importjava.io.Serializable;publicclassHelloTaskImplimplementsHelloTask,Serializable{privatestaticfinallongserialVersionUID=1L;publicStringexecute(){returnHelloWorld;}}HelloTaskImpl代码:例一:HelloWordimportjava.rmi.RemoteException;publicinterfaceIHelloextendsjava.rmi.Remote{Stringsay(HelloTasktask)throwsRemoteException;}IHello接口代码:例一:HelloWordimportjava.rmi.RemoteException;importjava.rmi.registry.LocateRegistry;importjava.rmi.registry.Registry;publicclassHelloServerextendsjava.rmi.server.UnicastRemoteObjectimplementsIHello{privatestaticfinallongserialVersionUID=2279096828129284306L;publicHelloServer()throwsRemoteException{super();}publicStringsay(HelloTasktask)throwsRemoteException{Stringresult=task.execute();System.out.println(executesay,task.execute:+result);returnresult;}publicstaticvoidmain(String[]args){try{HelloServerh=newHelloServer();Registryregistry=LocateRegistry.createRegistry(2500);registry.bind(hello,h);System.out.println(Start...);}catch(Exceptione){e.printStackTrace();}}}服务器端代码为:例一:HelloWord客户端代码为:importjava.rmi.Naming;publicclassHelloClient{publicstaticvoidmain(String[]args){try{IHellohi=(IHello)Naming.lookup(rmi://127.0.0.1:2500/hello);HelloTasktask=newHelloTaskImpl();for(inti=0;i10;i++){System.out.println(hi.say(task));}}catch(Exceptione){e.printStackTrace();}}}运行结果为:例二客户端通过服务器端执行算术运算,Calculator接口的代码为:importjava.rmi.Remote;publicinterfaceCalculatorextendsRemote{publiclongadd(longa,longb)throwsjava.rmi.RemoteException;publiclongsub(longa,longb)throwsjava.rmi.RemoteException;publiclongmul(longa,longb)throwsjava.rmi.RemoteException;publiclongdiv(longa,longb)throwsjava.rmi.RemoteException;}CalculatorImpl.java实现了Calculator的接口,代码为:importjava.rmi.server.UnicastRemoteObject;publicclassCalculatorImplextendsUnicastRemoteObjectimplementsCalculator{publicCalculatorImpl()throwsjava.rmi.RemoteException{super();}publiclongadd(longa,longb)throwsjava.rmi.RemoteException{returna+b;}publiclongsub(longa,longb)throwsjava.rmi.RemoteException{returna-b;}publiclongmul(longa,longb)throwsjava.rmi.RemoteException{returna*b;