科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道保护WebLogic Server 9.2上的Web Services

保护WebLogic Server 9.2上的Web Services

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

在现今的IT领域,Web services变得日益重要,特别是随着面向服务的架构(SOA)的快速发展和应用。默认情况下,Web Services调用(请求和响应)采用纯文本通过HTTP之类的协议传送。

来源:dev2dev 2007年10月14日

关键字: web BEA Weblogic 中间件

  • 评论
  • 分享微博
  • 分享邮件

摘要

  在现今的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的结合为这个问题提供了一个极好的解决方案,使开发人员得以轻松地部署和维护企业服务。

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章