12.3 实例:Web Service应用开发安全
相信大多数读者对于开发Web Service系统并不陌生。通过Web Service我们可以获得天气预报、火车时刻表、股票行情、电视节目预告等相应数据。公司之间的合作也常常通过Web Service系统完成数据同步等。
如何保护这样一个基于明文协议(WSDL、SOAP协议等)的系统是每一个架构师不容忽视的安全问题。
如果对传输的对象加密,或者对传输数据做加密处理,虽然可以保证在接口开放的前提下保护敏感数据,但却加大了安全开发的工作量,同时也将带来许多新问题:密钥的管理、数据交互双方算法的商榷、敏感数据范围的定义等。
使用数字证书构建SSL/TLS协议,最终构建HTTPS服务是保护Web Service系统的最佳方案。
开源组织Apache为我们提供了非常丰富的Web Service框架(http://ws.apache.org/),其中尤以Axis和CXF最为常用。Axis框架包含两个分支:Axis(http://ws.apache.org/axis/)和Axis2(http://ws.apache.org/axis2/)。CXF框架(http://cxf.apache.org/)由Celtix(http://celtix.ow2.org/)和XFire(http://xfire.codehaus.org/)发展而来,准确地说,CXF=Celtix+XFire。Spring也提供了自己的Web Service产品—Spring Web Service。
本文以开源Web Service架构Axis 1.4为例,介绍如何构建单向认证服务和双向认证服务,确保Web Service开发安全。
12.3.1 Web Service应用基本实现
我们在Eclipse中构建一个动态Web项目,用于构建简单的Web Service应用。
1.准备工作
根据Axis官方文档说明,我们将用到以下jar包:
❑axis.jar:Axis核心包。
❑activation.jar和mail.jar:用于电子邮件操作。
❑jaxrpc.jar:用于基于XML的远程过程调用。
❑commons-discovery.jar:用最佳的算法查找某个接口的所有已知的实现。
❑commons-logging.jar:用于日志控制。
❑wsdl4j.jar:生成/解析WSDL协议。
请读者朋友在Axis官方网站下载完整Axis发行包,上述jar包均可以在该发行包中找到。
接下来,我们需要配置web.xml文件,完整内容如代码清单12-29所示。
代码清单12-29 web.xml文件
<?xml version="1.0"encoding="UTF-8"?>
<web-app
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID"
version="2.5">
<display-name>Apache-Axis</display-name>
<servlet>
<display-name>Apache-Axis Servlet</display-name>
<servlet-name>axis</servlet-name>
<servlet-class>org.apache.axis.transport.http.AxisServlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>axis</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>
</web-app>
org. apache.axis.transport.http.AxisServlet类是我们唯一需要配置的Servlet。这里我们将请求路径位于“/services/”下的任何请求都交给AxisServlet类处理。
org. apache.axis.Version类提供了一个用于获得Axis版本信息的静态方法getVersion()。我们可以将该方法暴露在Web Service中,构建一个简单的Web Service。当我们访问地址http://localhost:8080/axis/services/Version?wsdl时,将得到相应的WSDL(Web Service Definition Language, Web Service描述语言)协议。
Axis Web Service相关配置位于WEB-INF目录下的server-config.wsdd文件中。文件详情如代码清单12-30所示。
代码清单12-30 server-config.wsdd文件
<?xml version="1.0"encoding="UTF-8"?>
<deployment
xmlns="http://xml.apache.org/axis/wsdd/"
xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
<transport
name="http">
<requestFlow>
<handler
type="java:org.apache.axis.handlers.http.URLMapper"/>
</requestFlow>
</transport>
<transport
name="local">
<responseFlow>
<handler
type="java:org.apache.axis.transport.local.LocalResponder"/>
</responseFlow>
</transport>
<service
name="Version"
provider="java:RPC">
<parameter
name="allowedMethods"
value="getVersion"/>
<parameter
name="className"
value="org.apache.axis.Version"/>
</service>
</deployment>
注意上述配置中的“service”节点,这里我们将暴露一个名为“Version”的服务。该服务指向org.apache.axis.Version类,仅允许使用getVersion()方法完成相应操作。
现在,我们启动Tomcat并访问地址http://localhost:8080/axis/services,将得到Web Service列表,如图12-18所示。
图 12-18 Web Service列表1
继续点击上述页面链接(wsdl)获得Version对应WSDL协议,如图12-19所示。
通过上述页面的描述,我们已经很清楚:可以通过调用getVersion()方法获得String类型的返回值。
图 12-19 WSDL协议1
2.验证服务
现在,我们构建相应的SOAP(Simple Object Access Protocol,简单对象访问协议)响应测试用例,验证上述服务。
Axis框架提供的Web Service客户端访问需要先构建Service实现类,并通过该类的createCall()方法创建调用对象Call类实例化对象。完整代码如代码清单12-31所示。
代码清单12-31 构建调用对象
//创建调用对象
Service service=new Service();
Call call=(Call)service.createCall();
接下来,我们需要对Call类实例化对象call做相应设置:配置远程调用的方法(通过调用setOperationName()方法)和设置访问URL(通过调用setTargetEndpointAddress()方法)。完整代码如代码清单12-32所示。
代码清单12-32 配置远程调用对象
//调用远程方法
call.setOperationName(new QName(namespaceUri,"getVersion"));
//设置URL
call.setTargetEndpointAddress(new URL(wsdlUrl));
最后,我们需要通过invoke()方法完成调用,并获得返回值。完整代码如代码清单12-33所示。
代码清单12-33 执行远程调用
//执行远程调用,同时获得返回值
String version=(String)call.invoke(new Object[]{});
有关Axis框架的相关实现,请读者查阅相关API文档。测试用例完整代码如代码清单12-34所示。
代码清单12-34 SOAP响应测试用例
import static org.junit.Assert.*;
import java.net.URL;
import javax.xml.namespace.QName;
import org.apache.axis.client.Call;
import org.apache.axis.client.Service;
import org.junit.Before;
import org.junit.Test;
/**
*WebService测试
*@author梁栋
*@version 1.0
*@since 1.0
*/
public class WebServiceTest{
//Namespace URL
private String namespaceUri="http://localhost:8080/axis/services/Version";
//WSDL URL
private String wsdlUrl="http://localhost:8080/axis/services/Version?wsdl";
/**
*测试
*@throws Exception
*/
@Test
public final void test()throws Exception{
//创建调用对象
Service service=new Service();
Call call=(Call)service.createCall();
//调用远程方法
call.setOperationName(new QName(namespaceUri,"getVersion"));
//设置URL
call.setTargetEndpointAddress(new URL(wsdlUrl));
//执行远程调用,同时获得返回值
String version=(String)call.invoke(new Object[]{});
//打印信息
System.err.println(version);
//验证
assertNotNull(version);
}
}
如果调用验证通过,我们将在控制台得到Axis的版本信息,如图12-20所示。
图 12-20 Axis版本信息
3.网络监测
在这里,作者为方便通过Wireshark监测网络传输信息,将Tomcat应用部署在虚拟机(IP为192.168.184.131),并通过IP直接访问。
首先,我们需要找到要绑定的网卡,并单击“Start”按钮进行绑定监听。作者使用的虚拟机网卡IP为192.168.184.1,请读者根据实际情况绑定对应网卡。网卡绑定如图12-21所示。
图 12-21 网卡绑定
完成网卡绑定后,在过滤器地址栏中输入过滤信息,如代码清单12-35所示。
代码清单12-35 过滤拦截1
(ip.src==192.168.184.131&&ip.dst==192.168.184.1)||(ip.dst==192.168.184.131
&&ip.src==192.168.184.1)&&http
其中
(ip. src==192.168.184.131&&ip.dst==192.168.184.1)指限定由本机请求虚拟机
(ip. dst==192.168.184.131&&ip.src==192.168.184.1)指限定由虚拟机回复本机
http 指定HTTP协议
重新执行SOAP请求,我们将在Wireshark中获得详细的SOAP请求和回应信息,如图12-22所示。
右键点击图12-22中任意一条数据包(No.298或No.300),在弹出的菜单中选择“Follow TCP Stream”菜单项。
在弹出的窗口(Follow TCP Stream)中单击下拉列表框,选择请求和回复信息,分别得到SOAP请求和SOAP回复内容,如图12-23和图12-24所示。
图 12-22 SOAP请求和回应信息
图 12-23 SOAP请求
图 12-24 SOAP回复
如果我们仅仅将列车时刻表、天气预报和电视节目预报等公开服务通过Web Service系统暴露服务,将无须考虑任何安全问题。但如果我们需要通过Web Service系统与合作伙伴交互商业数据,这将是一件可怕的事情。