主题
企业架构
作者:Gary Ng 和 Matt Silver
06/05/2007
在现今的 IT 领域,Web 服务变得日益重要,特别是随着面向服务的体系结构 (SOA) 的快速发展和采用。默认情况下,Web 服务调用(请求和响应)采用纯文本通过 HTTP 之类的协议传输。虽然使用纯文本传输考虑到更广泛的客户端互操作性,但是也带来了安全隐患。本文讨论这样做所招致的一些安全性风险以及如何使用 BEA WebLogic Server 的 Web 服务安全性实现来避免这些风险。
本文由两大部分组成。第一部分概述消息完整性、消息机密性和身份验证,简短地讨论如何通过 策略批注 加以实现。第二部分是一个教程,通过一个示例分步指导读者构建一个基本服务,然后添加消息的完整性、机密性和授权。
假设读者熟悉 BEA WebLogic Server 9.2 上的基本 Web 服务开发,以及诸如数字签名、数字加密之类的基本安全概念。
本节概述 Web 服务安全性的许多方面,包括消息的完整性、机密性和身份验证。如果您熟悉这些内容,可以直接阅读教程部分。
一个基本的 Web 服务由两部分组成:
本文中,我们将假定服务提供者是部署(和运行)在 WebLogic Server 9 实例上的一个基于 Java 的 Web 服务。服务使用者将是位于本地或远端计算机上运行在它自己的 JVM 中的一个 Java 客户端。
Web 服务交互首先由客户端从服务提供者获得 WSDL 文件。WSDL 文件包含客户端调用服务所需的服务和调用信息。一旦客户端有了 WSDL 文件,它就能够向服务提供者发送一个纯文本 SOAP 请求消息(包含要调用的服务和任何参数等信息)来调用一个 Web 服务。提供者收到该消息并进行分析,然后进行处理。处理一结束,提供者就向客户端返回一个纯文本 SOAP 响应消息(包含所有结果数据)。
由此可见,纯文本 SOAP 是默认的传输协议。遗憾的是,这种协议非常容易受到各种安全性攻击。我们要求助于 WS-Security。
由 OASIS 定义和维护的 WS-Security 概述了 Web 服务需要解决的安全性的三个关键部分。具体而言,当请求和响应消息进行交换时,需要考虑以下三个方面:
消息完整性 确保消息来源是唯一的,在传输过程中不被篡改。这是通过传出的 SOAP 消息 签名 实现的。签名通常包含附加的数字证书(加密的纯文本块),它表示一个唯一的消息散列。接受方收到消息后,可将消息的内容与这个散列进行比较;如果匹配,说明消息在传输过程中未被篡改。
消息机密性 确保只有接收方能够理解发来的消息。这是通过对传出的 SOAP 消息加密实现的。
身份验证 确保只有通过身份验证的客户端才允许调用 Web 服务。这是由客户端在服务调用中增加 WS-Security 令牌 (例如,用户名/口令、X.509 认证和 Kerberos 票证)实现的。然后这个令牌在请求时经服务器的安全系统验证,只有确认有效,服务才会处理请求。
默认的 SOAP 规范本身不具有这些功能。虽然手动(通过编程)添加对这些特性的支持是可能的,但是这会耗费时间且容易出错。幸而 WS-Security 规范描述了一个相应的实现,它利用 SOAP 头的固有可扩展性。几乎不用编码,就能够在 Web 服务消息交换中增加完整性、机密性和身份验证。
WebLogic Server 9.2 完全支持 WS-Security 标准。也就是说对 Web 服务提供者和客户端的代码编写方式略加更改,即可实现消息的完整性、机密性和身份验证。
WebLogic Server API 使得启用安全性十分容易,实现所有这三个安全性方面的基本步骤是:
密钥存储器实质上就是一个唯一、安全地标识服务器的证书集合。使用随 JDK 提供的 keystore 命令行工具可以创建密钥存储器。使用 WebLogic Server 管理控制台可以将密钥存储器导入 WebLogic Server 中。
这三方面可以任意组合使用(例如,可以仅启用完整性、机密性和身份验证三者之一,也可以三者都启用)。本文将示范如何实现各方面。
WebLogic Server 9.2 使用策略文件维护 Web 服务安全性。策略文件是一个 XML 文件,说明 WebLogic Server 如何尝试启用安全性的各个方面。Web 服务由策略文件包装,这些策略文件在部署过程中嵌入所生成的服务的 WSDL 文件之中。当客户端试图调用服务时,它应该首先获得该服务的 WSDL 文件连同策略信息。
WebLogic Server 安全性使用三个策略文件。
Sign.xml — 启用消息完整性所必需的。该文件包含配置信息,指明如何在客户端和服务器之间进行消息签名。Encrypt.xml — 启用消息机密性所必需的。该文件包含配置信息,指明如何在客户端和服务器之间进行消息加密。Auth.xml — 启用身份验证所必需的。该文件包含配置信息,指明如何对客户端和/或服务器进行身份验证。在部署时,这些策略文件必须由 Web 服务再次打包。当然,只要求所需的安全性方面的策略文件(例如,如果只要求消息的机密性,则只需要打包 Encrypt.xml,其余两个被忽略)。
然后,我们关心的一个问题是如何创建这些策略文件。BEA 提供了这些策略文件的文档和模式,可以用任何文本编辑器手动编写它们。然而,一个好消息是 这些策略文件能够自动生成。适当地对源代码加批注并运行 jwsc Ant任务即可生成所需的 sign.xml/encrypt.xml/auth.xml 文件。随后,这些自动生成的文件将由可部署的 Web 服务自动打包。
通常,这些自动生成的策略文件足以满足要求。它们的内容将反映在初始源中的批注;那么,保护 Web 服务安全的开发人员只需要考虑确保正确地为源代码加批注。开发人员不必关心策略文件的创建。
上面说过,有时自动生成的策略文件可能是不可用的。可能需要对安全性框架进行一些额外的定制。例如,要求选择性地只加密消息的一部分而不是全部,默认情况下,自动生成的策略会加密整个消息。在这种情况下,开发人员可以自由地手动创建策略文件,然后批注源代码来使用手动创建的策略文件。这会阻止自动文件生成。然而,一般不需要这样,因为自动生成的策略完全够用(并且只要极少的工作量)。
回想典型 Web 服务的开发,其中涉及到 Java 组件(例如,POJO 或 EJB)编码,然后 批注 产生 Java Web 服务 (JWS) 的代码。为了实现服务的安全性,要将 @Policies 和 @Policy 批注添加到 JWS 中。
例如,您可以将以下批注添加到 JWS:
@Policies({
@Policy(uri="policy:Sign.xml"),
@Policy(uri="policy:Encrypt.xml"),
@Policy(uri="policy:Auth.xml")
})
使用这些批注表示该 Web 服务需要所有三个方面:完整性、机密性和权限。当然,每个批注都有几个可添加的可选属性。稍后将介绍这些属性。
介绍完基础知识后,我们将进入一个示例,看看如何创建 Web 服务并为之添加各安全性方面。
在本教程中,我们将创建一个简单的“Hello World”类型的服务,然后为之添加安全性方面。我们的 Web 服务容器是 WebLogic Server,开发环境是免费的开源 WTP IDE。整个处理过程包括以下基本步骤:
其中某些步骤(例如,配置 WebLogic Server 环境)并非本文讨论重点,此处不再赘述;有关如何实现这些步骤的更多信息,请参见相关 BEA 文档。
如果没有安装 WebLogic Server,请先安装。使用 BEA 配置向导创建新的 WebLogic 域,管理用户名是 weblogic,口令是 password。无需特殊设置(例如,集群、受管理的服务器、JDBC 和 JMS),所以可以跳过相关步骤。将该域命名为 WSTestDomain,并将之留在默认的目录中。这就创建了域目录。如果您使用的是基于 Windows 的操作系统,则域目录应该类似于 C:\bea\user_projects\domains\WSTestDomain。本教程中,此目录简称为 DOMAIN_DIR。
就本教程而言,我们将采用广泛应用的开源 Eclipse IDE,利用 Web 工具平台 (WTP) 插件。如果您还没有这些软件,请从 http://download.eclipse.org/webtools/downloads/ 处下载。 注意,本教程假定读者熟悉 WTP/Eclipse。如果不是这样,建议读者先看一下 Eclipse WTP 的帮助文档。
这里使用 WTP 是随意的。您可以选择使用任何 IDE(如若不然,也可采用基于文本编辑器和命令行的环境,虽然基于文本的环境不允许检查消息流 — 为此需要一个额外的工具)。本文选择 WTP 的原因只是因为它得到了广泛应用,而且是开源的(即,可以免费下载和使用),还因为它支持 TCP/IP 监视器功能,允许我们查看实际传输的受保护消息。
安装 WTP 通常需要获得包含该产品的 ZIP 文件,然后将它解压缩到文件系统中。之后,运行解压缩后的文件。出现提示后,选择新的工作区目录(例如, C:\workspaces)。在本教程中,该目录简称为 WORKSPACE_DIR。
启动 WTP 之后,您需要将默认的 JVM 更改为 WebLogic Server 附带的JVM。切换到 Java 透视图(除非它已经是活动的透视图)。在菜单中选择 Window | Preferences。在左侧窗格中,展开 Java 并在它下面选择 Installed JREs。我们需要添加 BEA 提供的 JDK。在右侧窗格中,单击 Add...。单击 Browse... 按钮,导航到您的 BEA JDK 主目录(通常为 C:\bea\jdk150_04),然后单击 OK。现在,两个 JRE 均应列出。选中新添加的 JRE (jdk150_04) 的框,然后单击 OK。如果出现提示要求您重新构建,则单击 Yes。
至此已经配置完 WTP,可以激活监视器了。
为了测试安全措施是否按要求工作,我们将激活 WTP 的 TCP/IP 监视器功能。这将允许我们截取和检查客户端与服务器间正在交换的消息。为此,我们必须定义一个从 WTP 内部指向 WebLogic Server 的服务器实例。首先,停止所有当前运行的 WebLogic Server 实例(使用 stopWebLogic.cmd 脚本)。
在 WTP 中,打开 Servers 视图。右键单击视图任意位置,选择 New | Server。在弹出的菜单中,展开 BEA Systems 并选择 Generic BEA WebLogic Server v9.2。单击 Next。在 JRE 下拉菜单中,选择 jdk150_04,然后单击 Next。
更改 Domain Directory,使其指向 DOMAIN_DIR。更改 Start Script 和 Stop Script 域,使其指向 DOMAIN_DIR/bin 中对应的启动/停止脚本( startWebLogic.cmd 脚本和 stopWebLogic.cmd 脚本)。将 Password 更改为 password,然后单击 Finish。 回到 Servers 视图,右键单击新出现的 Generic BEA WebLogic Server v9.2 并选择 Monitoring | Properties。在出现的弹出式 Monitoring Ports 窗口中,单击 Add...。在之后出现的弹出式菜单中,只需单击 OK。现在应该会看到列出了一个新的监视器端口 (7001/7002)。选择它,单击 Start。单击 OK。
此时,所有发往端口 7002 的业务都将传递给监视器,在转发给(正确的)服务器端口 7001 之前由显示器显示出来,以便我们检查传入和传出的流量。
现在我们要创建一个基本的 Web 服务 JWS。它是一个非常简单的服务。只需传入一个字符串,它就会返回适当的问候。然后,我们将构建和部署该服务。
在 WTP 中,创建一个名为 WSTest 的新 Java 项目。在该项目中,创建一个名为 com.test.HelloWorldService 的新 Java 类。
现在我们要向项目添加 BEA weblogic.jar 库,以便我们的代码可以正确编译。右键单击 WSTest 项目并选择 Properties。出现 Properties 窗口。单击左侧窗格中的 Java Build Path。单击右侧窗格中的 Libraries。单击 Add External JARs。在出现的J JAR Selection 窗口中,导航到您的 WebLogic Server 库文件夹并选择 weblogic.jar(通常类似于 C:\bea\weblogic92\server\lib\weblogic.jar)。单击 Open。现在,您应该会看到 weblogic.jar 列于 Libraries 中。单击 OK。
将 HelloWorldService.java 的源代码设置成如下所示:
package com.test;
import javax.jws.*;
@WebService(name = "HelloWorldPortType",
serviceName = "HelloWorldService",
targetNamespace = "http://mycompany.com")
public class HelloWorldService {
public String sayHello(String name) {
return "Hello there, " + name;
}
}
注意 @WebService 批注的使用,它将这个类定义为一个 Web 服务。还要注意一个 sayHello 业务方法,它捕获一个 String 作为参数,并返回一条相应的问候语。
下一步是创建用于构建和部署服务的 Ant 脚本。在项目目录( WORKSPACE_DIR/WSTest)中创建一个名为 build.xml 的文本文件,然后将其内容设置为如下所示:
<project default="build"> <taskdef name="jwsc" classname="weblogic.wsee.tools.anttasks.JwscTask" <//> <target name="jwsc"> <jwsc srcdir="." destdir="." > <jws file="com/test/HelloWorldService.java" /> </jwsc> </target> <target name="build" depends="jwsc"> <wldeploy action="deploy" name="HelloWorldService" source="com/test/HelloWorldService.war" user="weblogic" password="password" adminurl="t3://localhost:7001/" targets="AdminServer"/> </target> </project>
这里没什么特别值得注意的内容。定义了两个 target — 一个构建实际的服务,一个部署该服务。
现在可以运行该服务了。在 WSTestDomain 中启动管理服务器。(在 WTP 内的 Servers 视图中,右键单击 Generic BEA WebLogic Server v9.2,然后单击 Start 即可实现。) 打开一个命令 shell 窗口,执行位于 DOMAIN_DIR/bin 中的 setDomainEnv 脚本。然后,还在这个命令 shell 窗口中,键入 cd 进入 WORKSPACE_DIR/WSTest 目录。在该目录下运行 ant 命令。
脚本应该可以运行,您应该看到一条 BUILD SUCCESSFUL 消息。不要关闭命令 shell 窗口,后面还需要它。此时,服务应该被构建、部署并运行。打开一个浏览器,浏览到 http://localhost:7001/HelloWorldService/HelloWorldService?WSDL。应该会看到该服务的 WSDL 文件。将其加入书签,因为本教程始终都需要它来生成客户端代理。现在,使用浏览器在 WORKSPACE_DIR/WSTest 目录下将该文件保存为 HelloWorldService.wsdl(确保文件名末尾不会加上“.xml”扩展名)。