跳到导航
BEA Dev2Dev Oracle and BEA
首页 资源中心 dev2dev学堂 在线技术论坛 User Group CodeShare
dev2dev 首页 > 资源中心 > 技术文章
保护WebLogic Server 9.2上的Web Services

时间:2007-08-21
作者:Gary NgMatt Silver
浏览次数:
本文关键字:Web Services authentication JWS WTP Security WebLogic Server Message SOAP 消息完整性 消息保密 身份验证 Web服务 安全 密钥
文章工具
推荐给朋友 推荐给朋友
打印文章 打印文章

摘要

  在现今的IT领域,Web services变得日益重要,特别是随着面向服务的架构(SOA)的快速发展和应用。默认情况下,Web Services调用(请求和响应)采用纯文本通过HTTP之类的协议传送。虽然使用纯文本传送是考虑到更广泛的客户端互操作性,但是也带来了安全隐患。本文讨论了这样做所招致的一些安全风险以及怎样应用BEA WebLogic Server的Web Services安全性实现避免这些风险。

  本文由两大部分组成。第一部分概述消息完整性、消息机密性和身份验证,简短地讨论了怎样通过策略注释(policy annotation)加以实现。第二部分是一个教程,通过一个实例一步一步地指导读者构建一个基本的服务,然后添加消息的完整性、机密性和授权。

  我们假定读者熟悉BEA WebLogic Server 9.2上的基本Web Services开发,以及诸如数字签名、数字加密等基本的安全概念。

Web Services和安全性概述

  本节概述Web Services安全性的许多方面,包括消息的完整性、机密性和身份验证。如果您熟悉这些内容,那么可以跳过这一节,直接阅读 教程部分

Web Services基础

  一个基本的Web Services由两个组件组成:

  • 服务的提供者——实际的服务,由服务容器提供。
  • 服务的消费者——调用服务的客户端,既可以是本地的,也可以是远程的。

  本文中,我们将假定服务的提供者是部署(和运行)在WebLogic Server 9实例上的一个基于Java的Web Services。服务的消费者将是位于本地或远端计算机上且运行在它自己的JVM中的一个Java客户端。

  Web Services交互首先由客户端从服务提供者获得WSDL文件。WSDL文件包含客户端调用服务所需的服务和调用信息。一旦客户端有了WSDL文件,它就能够向服务提供者发送一个纯文本SOAP请求消息(含有要调用的服务和任何参数等信息)来调用一个Web Services。服务提供者收到这个消息并解析它,然后进行处理。处理一结束,服务提供者就返回给客户端一个纯文本SOAP响应消息(含有所有的结果数据)。

  由此可见,纯文本SOAP是默认的传输协议。遗憾的是,这种协议非常容易受到各种安全性攻击。我们要求助于WS-Security。

WS-Security

  由OASIS负责定义和维护的WS-Security概述了需要Web services解决的安全性的三个关键部分。特别是当请求和响应消息进行交换时,需要考虑以下三个方面:

  • 消息完整性
  • 消息机密性
  • 身份验证

  消息完整性保证消息来源是惟一的,在传输过程中不被篡改。这是通过外向SOAP消息签名实现的。签名典型地包含附加的数字证书(加密的纯文本块),它表示一个惟一的消息散列。当收到消息时,接收方可将消息的内容与这个散列相比较。如果匹配,说明消息在传输过程中没有被篡改。

  消息机密性保证只有接收方能够理解发来的消息。这是通过外向SOAP消息加密实现的。

  身份验证保证只有通过身份验证的客户才允许调用Web Services。这是由客户端在服务调用中增加WS-Security  令牌  (例如,用户名/密码、X.509证书和Kerberos票据)实现的。然后这个令牌在请求时经服务器的安全系统验证,只有确认合法,服务才会处理请求。

  默认的SOAP规范本身不具有这些功能。虽然手工(通过编程)添加对这些特性的支持是可能的,但是这会耗费时间而且容易出错。幸而WS-Security规范描述了一个相应的实现,它利用了SOAP报头的固有可扩展性,几乎不用编码,就能够在Web Services消息交换中增加完整性、机密性和身份验证。

WebLogic Server 9.2的安全性配置概述

  WebLogic Server 9.2完全支持WS-Security标准。也就是说对Web Services提供者和客户端的代码编写方式略加更改,即可实现消息的完整性、机密性和身份验证。

  WebLogic Server API使得启用安全性十分容易,实现所有这三个安全性方面的基本步骤是:

  • 编写和测试没有安全措施的服务。
  • 为了完整性和机密性,为客户端和服务器设立一个keystore。
  • 通过注释服务的源代码给服务增加安全性。
  • 构建和部署受保护的服务。

  Keystore实质上就是一个惟一、安全地标识服务器的证书集合。使用随JDK提供的keystore命令行工具能够创建keystore。使用WebLogic Server管理控制台可以将keystore导入WebLogic Server。

  这三方面可以任意组合使用(例如,可以仅启用完整性、机密性和身份验证三者之一,也可以三者都启用)。本文将示范如何实现各方面。

策略和安全性概述

  WebLogic Server 9.2使用策略文件维护Web Services的安全性。策略文件是一个XML文件,说明WebLogic Server怎样尝试启用安全性的各个方面。Web Services由策略文件包装,这些策略文件在部署过程中嵌入所生成的服务的WSDL文件之中。当客户端试图调用服务时,它应该首先获得该服务的含有策略信息的WSDL文件。

BEA的策略文件

  WebLogic Server安全性使用三个策略文件。

  • Sign.xml——启用消息完整性所必需的。它包含配置信息,规定怎样在客户端和服务器之间进行消息签名。
  • Encrypt.xml——启用消息机密性所必需的。它包含配置信息,规定怎样在客户端和服务器之间进行消息加密。
  • Auth.xml——启用身份验证所必需的。它包含配置信息,规定怎样对客户端和/或服务器进行身份验证。

  在部署过程中,这些策略文件必须再一次由Web Services包装。当然,只有所需的安全性方面的策略文件将被要求(例如,如果只要求消息的机密性,那么只需要包装Encrypt.xml,其余两个被忽略)。

创建WS-Policy文件

  然后,我们关心的一个问题是如何创建这些策略文件。BEA提供了这些策略文件的文档和模式,可以用任何文本编辑器手工编写它们。不过,一个好消息是  这些策略文件能够自动生成  。适当地注释源代码并运行jwsc Ant任务即可生成所需的sign.xml/encrypt.xml/auth.xml文件。随后,这些自动生成的文件将由可部署的Web Services自动包装。

  通常,这些自动生成的策略文件足以满足要求。它们的内容将反映在初始源中的注释;那么,Web Services安全性的开发人员只需要考虑确保正确地注释源代码。开发人员不必关心策略文件的创建。

  上面说过,有的时候,自动生成的策略文件可能是不可用的。可能需要对安全性框架进行一些额外的定制。比如说要求选择性地只加密消息的一部分而不是全部,缺省情况下,自动生成的策略会加密整个消息。此时,开发人员可以自由地手工创建策略文件,然后注释源代码来使用手工创建的策略文件。这会阻止自动文件生成。然而,一般不需要这样,因为自动生成的策略完全够用(并且只要极少的工作量)。

安全性注释

  回想典型Web Services的开发,其中涉及到Java组件(例如,POJO或EJB)编码,然后  注释  产生Java Web Services(JWS)的代码。为了实现一个服务的安全性,要将@Policy和@Policy注释添加到JWS中。

  例如,您可以将下面的注释添加到 JWS:

@Policies({
 @Policy(uri="policy:Sign.xml"),
 @Policy(uri="policy:Encrypt.xml"),
 @Policy(uri="policy:Auth.xml")
})

  使用这些注释表示该Web Services需要所有三个方面:完整性、机密性和权限。当然,每个注释都有几个可添加的可选属性。稍后将介绍这些属性。

  讲完了基础的知识,我们将进入一个实例,看看如何创建Web Services,并为之添加各安全性方面。

  

消息级安全教程

  在本教程中,我们将创建一个简单的“Hello World”类型的服务,然后为之添加安全性方面。我们的 Web Services容器是WebLogic Server,开发环境是免费的开源WTP IDE。整个处理过程包括如下基本步骤:

  • 配置WebLogic Server环境
  • 配置WTP
  • 激活TCP/IP监视器
  • 构建、生成并部署一个简单的Hello World服务
  • 为这个Hello World服务构建一个简单的客户端,并测试它
  • 生成客户端密钥
  • 将客户端证书导入服务器信任文件
  • 为服务添加完整性
  • 为服务添加机密性
  • 为服务添加身份验证

  其中某些步骤(例如,配置WebLogic Server环境)并非本文讨论重点,此处不再赘述;参考有关的BEA文档可获得关于如何实现这些步骤的更多信息。

配置WebLogic Server环境

  如果没有安装 WebLogic Server 9.2,请先安装。使用BEA配置向导创建新的WebLogic域,管理用户名是weblogic,密码是password。无需特殊设置(例如,集群、受管理的服务器、JDBC和JMS),所以可以跳过相关步骤。将该域命名为WSTestDomain,并将之留在默认的目录中。这就创建了域目录。如果您使用的是Windows操作系统,则域目录应该类似于  C:\bea\user_projects\domains\WSTestDomain。本教程中,此目录简称为DOMAIN_DIR

配置WTP

  就本教程而言,我们将采用广泛应用的开源Eclipse IDE,利用Web Tools Platform(WTP)插件。如果您还没有这些软件,请从这里下载 http://download.eclipse.org/webtools/downloads/。  注意,本教程假定读者熟悉WTP/Eclipse。如果不是这样,建议读者先看一下Eclipse WTP的帮助文档。  

  这里使用WTP是随意的。您可以选择使用任何IDE(如若不然,还可采用一个基于文本编辑器和命令行的环境,虽然基于文本的环境不允许检查消息流——为此需要一个额外的工具)。本文选择WTP的原因只是它得到了广泛应用,而且是开源的(即,可以免费下载和使用),还因为它支持TCP/IP监视器功能,允许我们察看被发送的实际的受保护消息。

  安装WTP通常包括获得包含该产品的ZIP文件,然后将它解压缩到文件系统里。之后,运行解压缩后的文件。出现提示后,选择新的workspace目录(如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。如果出现提示要求您重建,则单击Yes

  至此,配置完WTP,可以启动监视器了。

启动TCP/IP监视器

  为了测试安全措施是否按要求工作,我们将启动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之前由显示器显示出来,以便我们检查传入和发出的流量。

构建、生成和部署一个简单的Hello World服务

  现在我们要创建一个基本的 Web Services JWS。它是一个非常简单的服务。传入一个字符串,它就会返回适当的问候。下面来构建和部署这个服务。

  在WTP中创建一个名为WSTest的新Java项目。在这个项目中创建一个名为com.test.HelloWorldService的新Java类。

  现在我们要给项目添加BEA weblogic.jar库,以便我们的代码可以正确的编译。右键单击WSTest项目,选择Properties。出现  Properties  窗口。单击左边窗格中的Java Build Path。单击右边窗格中的Libraries。单击Add External JARs。在出现的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 Services。还要注意一个sayHello业务方法,它捕获一个String作为参数,并返回一条相应的问候语。

  下一步是创建用于构建和部署服务的Ant脚本。在项目目录(WORKSPACE_DIR/WSTest)中创建一个名为build.xml的文本文件,然后建立它的内容,如下:

package com.test;
<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窗口,进入WORKSPACE_DIR/WSTest目录,运行ant命令。

  脚本应该运行了,您应该看到一条BUILD SUCCESSFUL消息。不要关闭命令shell窗口,后面还需要它。这里,服务应该被构建、部署并运行。打开浏http://localhost:7001/HelloWorldService/HelloWorldService?WSDL,应该会看到这个服务的WSDL文件。将其加入书签,因为本教程始终都需要它来生成客户代理。现在,在WORKSPACE_DIR/WSTest目录下保存这个文件为HelloWorldService.wsdl(确保文件名后面不要加上“.xml”后缀)。

为HelloWorld服务构建一个简单的客户端并进行测试

  我们现在要构建一个简单的Java客户端来测试前面创建的服务。尽管能够用WebLogic Server 控制台工具测试这个服务,但最终还是要用客户端来测试其各安全性方面。

  回想一下,客户端要成功地调用服务,首先必须生成客户端代理。这可通过另一个Ant脚本来完成。在WORKSPACE_DIR/WSTest文件夹中创建一个名为gen-client.xml的新文本文件,并且加入下面的内容:

package com.test;
<project default="build-client">
 <taskdef name="clientgen"
 classname="weblogic.wsee.tools.anttasks.ClientGenTask"/>
 <target name="build-client">
 <clientgen
 wsdl="HelloWorldService.wsdl"
 destDir="."
 packageName="com.test.client"/>
 </target>
</project>

  从前面打开的命令shell窗口运行ant -buildfile gen-client.xml命令。您应该会看到一条BUILD SUCCESSFUL消息。这就生成了客户端代理。

  回到WTP,刷新WSTest项目,应该能看到新生成的com.test.client包。然而,WTP会报告新生成的代理存在一个错误。要修正它,就要为项目添加webserviceclient.jar库,添加方法与添加weblogic.jar相同(webserviceclient.jar与weblogic.jar位于同一目录)。现在就可以开始编写客户端的代码了。

  在WTP内的WSTest项目中创建名为com.test.client.HelloWorldClient的新java类,如下设置它的源:

package com.test;
import com.test.client.*;
public class HelloWorldClient {
 public static void main(String[] args) throws Throwable {
 com.test.HelloWorldService service = new HelloWorldService_Impl();
 HelloWorldPortType port = service.getHelloWorldPortTypeSoapPort();
 String greeting = port.sayHello("Gary");
 System.out.println("The greeting returned was: " + greeting);
 }
}

  这个客户端非常简单,仅使用客户代理类以获得到服务的引用,然后调用其sayHello方法。

  运行这段代码,会出现适当的控制台打印结果。

  返回的问候是:Hello there, Gary

  请注意,机关我们已经激活了TCP/IP监视器,但这个测试还不会用到它。这是因为客户端在端口7001直接找到服务器,而不是通过监视器端口7002。我们稍后将介绍怎样修改它。

  现在已经构建了非常简单的Web Services和客户端,但还不具有安全性机制。下面将加以介绍。

生成客户端密钥

  我们需要一组密钥来实现各种形式的消息完整性(和加密)。密钥构成证书的基础,证书用来惟一地对一条消息进行签名以保证消息的完整性。发送者可以在消息中附加自己的证书;接收者可以检查证书,核对发送者身份,确定消息在传输中未被篡改。

  客户端也需要随证书发送数字签名增加消息完整性。单独的证书本身是没有意义的。客户端获得消息的散列,然后用专用密钥加密它,从而生成数字签名。然后,服务器使用消息中附加的客户证书(即,公用密钥)解密这个散列,证明它来自客户端。下一步,对比解密的散列和得到的消息散列。如果二者等同,则证明消息未被篡改。本教程有两个通信方:WebLogic Server和客户端。双方都需要密钥。

  WebLogic Server有自己的示范用的“dummy”密钥,虽然不能用于生产,但是本教程用其进行示范就足够了。这里我们将使用这些dummy密钥,但请记住,在实际环境中应该使用新生成的密钥。我们马上要用到keytool命令为客户端和服务器创建密钥。

  现在来创建客户端密钥。具体来说,我们将创建一对客户端密钥(公钥和相关联的私钥)。这对密钥中的私钥将用作消息的解密和数字签名。密钥对保存在keystore文件里。Keystore随密钥对的创建而创建。如果已经存在指定的keystore,则只需要将新的密钥对加进去。公钥在X.509  证书  内。证书和私钥保存在keystore中,用一个别名来识别。

  打开一个新的命令shell,进入BEA JRE bin目录(通常类似于C:\bea\jdk150_04\jre\bin)。然后键入下面的命令:

  keytool -genkey -keyalg RSA -keystore C:\client_keystore.jks -storepass abc123 -alias client_key -keypass client_key_password -dname "CN=Client, OU=WEB AGE, C=US" -keysize 1024 -validity 1460

  (此命令将在根目录下创建keystore。根据需要,可以改变它的位置。如果您使用的是基于Unix的操作系统,可以用更标准的Unix命名文件系统规则代替“C:\”。)

  创建的keystore名称是C:\client_keystore.jks,访问密码是abc123。密钥对的别名是client_key,私钥的保护密码是client_key_password。创建密钥对的算法是RSA。与别名client_key相关联的特殊命名是CN=Client、OU=WEB AG和C=US,在证书中用作发证者和主题域。密钥长度为1024 位(BEA要求的最小长度)。最后,对应的证书有效期为1460天(4年)。成功的话,C:\下应该出现一个名为client_keystore.jks的新文件。这就是客户端要用到的keystore。

  现在,我们将要检验这些密钥是否正确地创建了。在命令提示符窗口键入:

  keytool -list -keystore C:\client_keystore.jks -storepass abc123 -v | findstr Alias

  这时应显示:

  Alias name: client_key

  Keystore已经成功地创建了。客户端keystore包含一个名为client_key的实体的私钥和一个公钥(也称证书)。数字签名和加密都需要它们。

  请勿关闭第二个命令shell窗口。稍后还要使用它。

将客户端证书导入服务器信任文件

  现在必须要告诉服务器信任客户端的证书。

  WebLogic Server维护着一个信任文件,实质上就是它要信任和接受的证书列表。遗憾的是,我们前面生成的客户端密钥不在信任文件里。这意味着我们的客户端证书如果被WebLogic收到,会被立刻拒绝。我们需要做的是将客户端证书导入服务器信任文件。

  这个过程分两个阶段:首先,必须从客户端的keystore导出证书,然后将其导入服务器信任文件。当前配置的服务器使用一个“demo”信任文件(Demotrust.jks),它又引用标准JDK信任文件。标准JDK信任文件一般位于:

  C:\bea\jdk150_04\jre\lib\security\cacerts。(注意,根据您的操作系统和安装设定不同,这个目录可能会有不同。)

  我们需要将客户端证书导入这个cacerts文件。

  首先,从client_keystore.jks导出客户端证书。在生成密钥时打开的命令shell窗口中,键入以下命令:

  keytool -export -alias client_key -file client_cert.der -keystore C:\client_keystore.jks -storepass abc123

  第二步,将证书导入服务器信任文件。键入以下命令:

  keytool -import -alias client_key -file client_cert.der -keystore C:\bea\JDK150~1\jre\lib\security\cacerts

  (如果您的cacerts文件不是位于C:\bea\JDK150~1\jre\lib\security\,那么请相应地修改命令。)

  出现提示后键入密码changeit。(changeit是WebLogic Server附带的默认密码。当然,在生产中应当换成更安全的密码。)

  接着会出现提示this certificate [no]:。键入 y,按回车。

  如果一切顺利,您将看到Certificate was added to keystore消息。

  此时,客户端的证书已经加入服务器信任文件。现在服务器会信任使用这一证书的任何客户端。

  关闭运行keytool命令的命令shell窗口。

为服务加入完整性

  既然有了客户端密钥,现在来看看它将如何帮助我们实现消息完整性。如果一方发送SOAP消息并在外发消息中附加证书,则接收方能够阅读证书,确信地识别发送者,确保消息在传输中未被篡改。这是通过客户端随SOAP请求发送自己的证书实现的。(回想一下,发出的证书也有一个消息的散列摘要。被篡改后,消息的散列将有所是不同的,服务器能检测出来。这就保证了消息的完整性。)

  为了利于它实现,我们必须进行两项修改:

  • 必须编辑服务类,以便请求签名。
  • 必须编辑客户类,以便在请求中附加签名。

  使服务请求消息完整性非常简单,只需要向源代码添加一个单独的注释。切换回WTP,打开HelloWorldService.java。添加下面突出显示的代码:

import javax.jws.WebService;
import weblogic.jws.Policies;
import weblogic.jws.Policy;
@WebService(name = "HelloWorldPortType",
 serviceName = "HelloWorldService",
 targetNamespace = "http://mycompany.com")
@Policies({
 @Policy(uri="policy:Sign.xml")
})
public class HelloWorldService {
 public String sayHello(String name) {
 return "Hello there, " + name;
 }
}

  我们所作的一切就是加入了一个单独的注释(@Policies/@Policy)并导入了所需的适当的包。这个注释将确保生成的Web Services总是要求客户端对其SOAP请求进行签名。保存所做的修改。这部分应该不会出现错误。

  现在需要重新生成服务。使用前面生成服务而打开的命令窗口,运行ant命令。您将看到BUILD SUCCESSFUL消息;服务应该被生成、打包和部署了。

  由于修改了服务,我们还要基于新生成的WSDL文件重新生成客户代理。回到浏览器,再次打开WSDL文件的URL。将它保存为WORKSPACE_DIR\WSTest\HelloWorldService.wsdl。用文本编辑器检查这个文件。注意,那里现在有一个名为wssp:Integrity的元素,它包含了相当多的信息,其中有它信任的内容。如果您遍历wssp:TokenIssuer元素体,应该看见一个到CN=Client,OU=WEB AGE, C=US的引用,它确实是我们的客户端密钥中生成的,之后导入WebLogic Server信任文件的信息。还请注意WSDL文件包括的新的Sign.xml策略:

<wsp:Policy s0:Id="Sign.xml">
...
</wsp:Policy>
...
 <portType name="HelloWorldPortType" wsp:PolicyURIs="#Sign.xml">
  ..
  </portType>

  WSDL文件末尾处有如下代码:

  <s2:address location="http://localhost:7001/HelloWorldService/HelloWorldService"/>

  如下将端口号改为7002:

  <s2:address location="http://localhost:7002/HelloWorldService/HelloWorldService"/>

  为何要这样做?原因很简单。我们希望客户端请求找到端口7002——监视器端口。保存并关闭WSDL文件。

  从命令shell窗口运行ant -buildfile gen-client.xml命令。您应该看到熟悉的BUILD SUCCESSFUL消息。现在重新生成了客户代理。

  回到WTP中,刷新WSTest项目。再次运行HelloWorldClient类。发生了什么?一个异常。检查出现的堆栈跟踪,找到Failed to add Signature消息。这说明服务要求证书,但是客户端没有附上。我们要修改客户端代码,以便调用服务时确实能够附上证书。

  打开WTP中的HelloWorldClient.java。添加下列import语句。

import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import javax.xml.rpc.Stub;
import weblogic.security.SSL.TrustManager;
import weblogic.wsee.security.bst.ClientBSTCredentialProvider;
import weblogic.wsee.security.unt.ClientUNTCredentialProvider;
import weblogic.xml.crypto.wss.WSSecurityContext;
import weblogic.xml.crypto.wss.provider.CredentialProvider;

  现在,向main方法添加以下突出显示的代码。

public static void main(String[] args) throws Throwable {
 com.test.client.HelloWorldService service = new HelloWorldService_Impl();
 HelloWorldPortType port = service.getHelloWorldPortTypeSoapPort();
 List credProviders = new ArrayList();
 CredentialProvider cp = new ClientBSTCredentialProvider(
 "C:\\client_keystore.jks", "abc123", "client_key",
 "client_key_password");
 credProviders.add(cp);
 Stub stub = (Stub) port;
 stub._setProperty(
 WSSecurityContext.CREDENTIAL_PROVIDER_LIST,
 credProviders);
 stub._setProperty(
 WSSecurityContext.TRUST_MANAGER, new TrustManager() {
 public boolean certificateCallback(X509Certificate[] chain,
 int validateErr) {
 // Put some custom validation code in here.
// Just return true for now
 return true;
 }
 });
 String greeting = port.sayHello("Gary");
 System.out.println("The greeting returned was: " + greeting);
 }
 

  保存代码。这里应该不会出现错误。现在运行更新的HelloWorldClient类。控制台应该返回与之前一样的信息,表明服务工作正常。然而,在后台还发生了一些别的事。回到WTP,应该看到TCP/IP Monitor视图。单击它,更改RequestResponse窗格来显示XML。

  检查Request窗格的内容,它将客户端发送的信息表示成外发SOAP请求消息。注意,它已经附加了一个含有客户证书(dsig:KeyInfo元素引用的wsse:BinarySecurityToken)、实际的SOAP体(dsig:DigestValue)的单向散列(也称报文摘译)和数字签名(dsig:Signature)的安全报头(wsse:Security)。消息已经签名了。您会注意到一个消息的摘要(dsig:DigestValue),它是实际的SOAP体的加密的单向散列。

  同样地,检查Response窗格。注意服务器返回的消息也附加了证书;这是服务器证书。为何要这样做?当注释服务类时,您指定了Sign.xml策略,默认规定传入和外发的签名——所以客户端和服务器的签名交换。

  既然已经交换了签名,那么任一方都能够验证消息确实来自指定的发送者,并且没有被篡改(通过检查摘要)。我们已经实现了  消息的完整性  。然而,请注意,消息的soapenv:body依旧是纯文本;没有消息的机密性(加密)。您能够清楚地看到SOAP 请求中的<name>元素和SOAP响应中的<return>元素。

为服务加入机密性

  现在我们要为服务加入机密性。这意味着希望在客户端和服务器之间加密消息(SOAP体)。其基本过程与添加消息的完整性一样:注释并重新生成服务,然后更新客户端。

  打开WTP中的HelloWorldService.java。添加下面突出显示的注释:

@Policies({
 @Policy(uri="policy:Sign.xml"),
 @Policy(uri="policy:Encrypt.xml")
})

  (别忘了在Sign.xml注释后添加逗号。)

  这个注释指定我们希望客户端和服务器使用加密。默认加密是双向的(即,从客户端到服务器、从服务器到客户端两个方向)。如果只要求单向加密,可向注释添加可选的direction属性来处理这一问题。

  现在需要重建服务。从命令窗口运行ant命令。如前,您将看到BUILD SUCCESSFUL消息。

  现在,更新客户端。首先,像以前那样获得WSDL文件并保存。用编辑器打开它。您将注意到一个新的元素:wssp:Confidentiality。这示意客户端使用加密。还请注意如下所示的Encypt.xml策略的内容:

<wsp:Policy s0:Id="Encrypt.xml">
...
</wsp:Policy>
...
 <portType name="HelloWorldPortType" wsp:PolicyURIs="#Sign.xml #Encrypt.xml">
..
</portType>

  由于新的部署重新生成了WSDL文件,我们需要再为监视器更新端口号。WSDL文件末尾处有如下代码:

  <s2:address location="http://localhost:7001/HelloWorldService/HelloWorldService"/>

  如下所示将端口号改为7002:

  <s2:address location="http://localhost:7002/HelloWorldService/HelloWorldService"/>

  保存并关闭WSDL文件。现在,从命令shell窗口运行ant -buildfile gen-client.xml命令,您会看到BUILD SUCCESSFUL消息,这时客户代理已经更新了。

  下面还要做什么呢?实际上不需要任何改动了。客户端无需添加任何代码使消息加密。客户端代理将提供所有的加密工作。这是使用Web services安全机制的一个主要优点;开发人员编写任何加密代码!

  运行HelloWorldClient,您将看到正常的问候响应。有什么改变了?初看起来似乎并无变化。然而,检查TCP/IP监视器,您将看到请求和响应已经加密了;无法像先前那样找到<name>元素。这里有<CipherData>元素和一些加密的文本。注意,只是加密了消息体(请求的name元素和响应的return元素);所有的消息报头没有加密。还请注意,安全报头(wsse:Security)已经添加了ns1:EncryptedKey元素。

  消息机密性已经被加入服务了!

为服务添加身份验证

  接着我们可以向Web Services添加授权机制。简单地说,我们可以强制客户端随SOAP请求提供用户名和密码;如果WebLogic Server的用户注册库没有这个用户名和密码,则服务器将拒绝对服务的访问。这允许一种形式的访问控制。注意这与消息的完整性中的身份识别不同。消息的完整性通过证书进行客户识别;权限涉及客户端是否实际有权调用服务。如果客户端未能成功地通过WebLogic Server的用户注册库身份验证却试图调用服务,则服务器将拒绝对服务的访问。

  下面我们要为WebLogic Server的用户注册库加入一个用户,然后修改服务强制客户端进行身份验证。然后我们将使用客户端进行测试。

  第一步是在WebLogic Server的用户注册库中创建一个用户。用户注册库默认嵌入在WebLogic Server的LDAP服务器中,通过WebLogic Server管理平台,可以加入用户。确定服务器在运行,然后使用浏览器浏览 http://localhost:7001/console。使用用户名weblogic和密码password登录,此时将打开管理平台。

  在左边的Domain Structure窗格单击Security Realms。在右窗格中,单击myrealmMyrealm显示当前WebLogic Server的安全配置。我们将向这个安全配置添加一个测试用户。单击Users and Groups标签。单击New按钮。

  在Name字段中键入suzie。在Description字段中键入An authenticated Web service user。在PasswordConfirm Password字段中键入OK

  我们已经在WebLogic Server的用户注册库中创建了一个新用户。现在可以编辑服务,要求所有客户端给出用户名/密码(希望与注册库中的用户匹配)进行身份验证。打开WTP中的HelloWorldService.java,然后加入下面突出显示的部分。

@Policies({
 @Policy(uri="policy:Sign.xml"),
 @Policy(uri="policy:Encrypt.xml"),
 @Policy(uri="policy:Auth.xml"
 ,direction=Policy.Direction.inbound)
})

  新的注释指定强制实施授权策略,但只针对入站流量。(本例中,强制服务器对于客户端进行反向身份验证意义不大。)

  保存文件。应该不会出现错误。现在可以构建并部署服务了。从命令shell窗口运行ant命令。如前,将会看到BUILD SUCCESSFUL消息。

  既然已经重新生成服务,则必须像前面所做的那样重新生成客户端代理。像前面所做的那样获得并保存WSDL文件。用编辑器打开,将出现一个新的元素wssp:Identity。它示意客户端在调用服务时应该发送用户名和密码。另请注意如下所示的新Auth.xml策略的内容:

<wsp:Policy s0:Id="Auth.xml">
...
</wsp:Policy>
...
<input>
 <s2:body parts="parameters" use="literal"/>
 <wsp:Policy>
 <wsp:PolicyReference URI="#Auth.xml"/>
 </wsp:Policy>
</input>

  如前,由于新的部署重新生成了WSDL文件,我们需要更新监视器端口号。WSDL文件末尾处有如下代码:

  <s2:address location="http://localhost:7001/HelloWorldService/HelloWorldService"/>

  如下所示,修改端口号为7002:

  <s2:address location="http://localhost:7002/HelloWorldService/HelloWorldService"/>

  保存并关闭WSDL文件。现在,从命令shell窗口运行ant -buildfile gen-client.xml命令,会看到BUILD SUCCESSFUL消息。客户端代理已经更新了。

  回到WTP,刷新WSTest项目。运行HelloWorldClient类。会发生什么呢?

  javax.xml.rpc.soap.SOAPFaultException: Unable to add security token for identity

  客户端试图调用服务,但又不提供任何授权凭证。客户端代码需要加以修改,以便使用刚才创建的用户名/密码suzie/munchkin来调用服务。现在开始向HelloWorldClient.java的main方法添加下面突出显示的代码:

 stub._setProperty(
 WSSecurityContext.CREDENTIAL_PROVIDER_LIST,
 credProviders);
 String username = "suzie";
 String password = "munchkin";
 cp = new ClientUNTCredentialProvider(username.getBytes(),
 password.getBytes());
 credProviders.add(cp);
stub._setProperty(

  运行代码,会返回正常的问候消息。这样,我们就知道了客户端调用服务前被强制添加凭证。查看监视器,检查请求,应该看到一个新的部分:

<wsse:UsernameToken>
<wsse:Username>suzie</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/
  oasis-200401-wss-username-token-profile-1.0#PasswordText">munchkin</wsse:Password>
</wsse:UsernameToken>

  这些的确是我们提供的凭证。注意,这些凭证以纯文本发送。WS-Security规范没有涉及用户凭证加密;而是推荐采用传输层安全措施(即,保护整个TCP/IP套接字)。

  身份验证已经成功地加入Web Services了!

展望未来:WebLogic Server 10的安全性配置

  WebLogic Server 10最近已发布。对于现有的受保护的Web services,这意味着什么呢?幸运的是,变化不是很多。第十版中服务的部署和运行没有变化。然而,客户端代码需要使用第十版的客户API重新生成。此外,WebLogic Server 10加入了WS-SecurityPolicy 1.2支持,现在它支持WS-Security 1.1。(WebLogic Server 9.2支持WS-Security 1.0。)

  WS-SecurityPolicy 1.2的重要性在于可以创建与BEA专有安全策略文件相对的“标准”安全策略文件。WS-SecurityPolicy规范发布之前发布的WebLogic Server 9.2使用的是依据采用专有BEA安全策略模式的旧版WS-Policy规范写成的安全策略文件。

  应该采用哪一种策略文件?BEA建议采用WS-SecurityPolicy标准策略文件代替其专有策略文件。实际上,这两种策略文件第十版都支持,但这二者是不兼容的,所以一个给定的Web Services只能采用二者之一。

  可是在您急着将WebLogic Server 9.2策略文件迁移到新标准之前,谨记WS-SecurityPolicy尚有局限之处,因为在BEA实现它的时候,它还只是(依然是)一个草案。具体来说,如果您需要元素级的数字签名和/或加密,或者要求SAML 1.1,那么您仍然必须采用专有的BEA WebLogic 9.2型策略。

  根据WS-Security,即使WebLogic Server 10支持WS-Security 1.1,BEA仍然推荐为了互操作性使用WS-Security 1.0策略。只是当您要求更强的安全性时,才应该采用WS-Security 1.1策略。而且只有使用新的WS-SecurityPolicy 1.2策略文件,WS-Security 1.1才被支持。

结束语

  在本文中,我们讨论了Web services安全性的三个方面:消息的完整性、机密性和身份验证。我们已经看到编写含有这三个方面的“安全”Web Services非常简单;所要做的只是用适当的注释来注释Web Services代码。我们了解了使用什么注释来实现每个方面,弄清了必须如何修改客户端代码调用受保护的服务,看到了SOAP消息自身如何改变以反映新加入的安全性。在教程中,我们建立了一个Web Services,并逐步加入所有三个方面。

  由于SOAP消息的“纯文本”的性质,当我们部署Web services作为企业生产环境的一部分的时候,强大的安全性是非常重要的。WS-Security与WebLogic Server的结合为这个问题提供了一个极好的解决方案,使开发人员得以轻松地部署和维护企业服务。

参考资料

  1. WebLogic Server 9.2 Administration——WebLogic Server管理指南
  2. WebLogic Server 10.0 Security——WebLogic Server安全性的文档主页
  3. Eclipse Documentation——关于使用Eclipse的文档
  4. Ant——Ant工具的官方网站
  5. OASIS Web Services Security——官方Web services安全规范之家
  6. Configuring Security——编写安全的Web services代码的BEA官方文档

原文出处:http://dev2dev.bea.com/pub/a/2007/06/securing-web-services.html

 作者简介

Gary Ng 是WebAge Solutions的高级顾问,自2001年以来从事过各种J2EE项目。

Matt Silver 是技术顾问兼培训师,目前是WebAge Solutions高级顾问。

dot dot dot

dot
  作者其它文章
您对本文的评价
您对这篇文章的看法如何?
太棒了!5分 不错啊 4分 一般般 3分 有待提高 2分 不好 1分

   
相关技术