使用安全的 Web 服务实现 Oracle WebLogic Server 11g 和 Microsoft.NET WCF 4.0 之间的互操作性

作者:Juan Carlos (John Charles) Olamendy Turruellas

了解如何创建支持 Oracle 与 Microsoft 技术之间互操作性的安全 Web 服务。

2010 年 10 月发布 

本文相关技术工具箱:


如今,Web 服务 (WS) 是分布式应用程序开发的主要模型,因为这些 Web 服务建立在开放、成熟的标准和技术(如 HTTP 和 XML)的基础上。

Web 服务自从引入以来,已用于解决许多业务问题,尤其是在集成组织内(编排)和组织外(设计)的异构系统领域。

主要的软件供应商(如 Oracle 和 Microsoft)在其平台和框架中均支持 Web 服务规范。

由于 Web 服务解决方案所面临的大量常见业务情形,Web 服务规范已开始采用新的标准扩展基本协议体系,以支持安全性、安全对话、可靠性、事务和策略实施等服务质量要求。

在使用 Web 服务集成异构应用程序方面不断遇到的一个挑战就是互操作性,因为各软件供应商纷纷针对 Web 服务规范做出了适合各自平台的诠释,有时这会导致一些问题。解决办法就是使用规则和最佳实践来实现可互操作的 Web 服务,如 WS-I 配置文件。

以下是许多组织中的一个常见互操作性情形:

  • 让信息系统运行在 Oracle WebLogic Server 容器中
  • 使用 Web 服务方法(支持 WS-* 协议体系)公开系统功能
  • 让客户端运行在 Microsoft Windows 环境中并以安全方式使用这些功能
 

本文介绍如何使用 Oracle 和 Microsoft 的新兴技术(尤其是使用 Oracle WebLogic Server 11g 和 Microsoft Windows Communication Foundation 4.0)实现 Web 服务解决此类情形的基础问题。

为了支持 WS-* 协议体系,需要一个强健的编程模型。Microsoft 提出的是 Windows Communication Foundation;而在 Java 世界,提出的是 JAX-WS。Oracle WebLogic Server 支持作为 Java EE 5 规范组成部分及作为在该平台上开发 WS 解决方案的主要方法的 JAX-WS。

为在业务情形中实现该解决方案,需要安全服务的技术基础,如身份验证、授权、机密性和完整性。讨论 Web 服务世界中的安全性时,主要分为两类:传输级安全性和消息级安全性。

传输级安全性是指依靠底层传输协议的安全机制来保护服务和客户端之间的连接。通常的方法是使用安全套接字层 (SSL),使两个应用程序通过网络进行连接,对另一方的身份进行验证并对交换的数据进行加密。使用传输级安全性的好处在于此方法基于被广泛采纳的成熟标准。

消息级安全性考虑的是依靠 WS-Security 规范保护每个简单对象访问协议 (SOAP) 消息,而不考虑消息所使用的是何种传输机制。

WS-Security 规范是一种端到端的机制,这意味着即使在传输涉及多个中介时,也能保证消息的安全。Oracle WebLogic Server 实现了 WS-Security(版本 1.0 和 1.1)以及 Username Token Profile(版本 1.0 和 1.1)、X.509 Token Profile(版本 1.0 和 1.1)和安全断言标记语言 (SAML) Token Profile(版本 1.0 和 1.1)。这些规范可以为 SOAP 消息提供安全令牌传播、消息完整性和机密性。

与 WS-Security 结合使用时,需要使用 WS-Policy 规范,该规范定义允许 Web 服务表示其安全限制的框架。消息级安全性的主要挑战在于该方法有待成熟,并且经常会导致互操作性问题。其主要优点在于在具有多种中介(如充当路由器的其他 Web 服务、企业服务总线 (ESB) 和工作流引擎)的复杂分布式环境中,此方法的可伸缩性更高。

为实现本文的业务案例,我们将使用 WS-Security、WS-Policy 和 WS-SecurityPolicy 规范来支持消息级安全,这些规范为身份验证、消息内容加密、数字签名以及高级互操作性提供了基础。

本文重点讨论如何为 Web 服务(位于 Oracle WebLogic Server 中运行的服务器端)定义 WS-Security 配置,该服务可以使用 X.509 证书进行身份验证、签名和加密来与运行于 WCF 环境中的客户端互操作。

开发解决方案的服务器端

为了集中讨论如何使用安全 Web 服务标准开发集成解决方案,我们的服务将非常简单,只是接收一个字符串消息然后将其返回到客户端。从概念上来讲,这就是回显服务。在实际应用中,Web 服务的实现将更为复杂,包括对向外界公开功能的业务对象的调用。

我们从服务器端应用程序的开发开始。打开 Oracle JDeveloper 11g IDE,创建一个新应用程序(参见图 1)。

olamendy-rest-f1 

图 1:在 Oracle JDeveloper 11g IDE 中新建应用程序

单击 OK 按钮。输入应用程序名、默认程序包和工作目录(参见图 2)。

olamendy-rest-f2 

图 2:为应用程序命名

单击 Next 按钮。Name your project 页面允许您为服务创建一个项目。(在 Oracle JDeveloper 中,应用程序包括多个项目。)为项目输入一个描述性名称和工作目录,并选择 WS 开发所用的主要技术(参见图 3)。

olamendy-rest-f3 

图 3:创建项目

单击 Next 按钮。在 Configure Java Settings 页面中,检查项目结构。最后,单击 Finish 按钮(参见图 4)。

olamendy-rest-f4 

图 4:配置 Java 设置

该解决方案的下一步是创建一个名为 EchoService 的基本 JAX-WS Web 服务。JAX-WS 规范是一种编程模型,它允许使用 Java 批注装饰基础 POJO 类的行为将 POJO 类的公有方法公开为 Web 服务操作以实现 Web 服务。

创建 JAX-WS Web 服务 (JWS) 的第一步是:选择 File | New 选项并单击 OK 按钮(参见图 5)。

olamendy-rest-f5 

图 5:添加类

出现 Create Java Class 窗口后,输入类名和程序包。单击 OK 按钮转至 Oracle JDeveloper 中的代码编辑器,您可以在此实现这个新类的业务逻辑。

我们的逻辑很简单,只是原样返回服务所接收的消息,因此无需任何业务对象。出于演示目的,我们选择直接在服务中实现这一简单逻辑,但值得一提的是,这在实际应用中并不是最佳做法(参见图 6)。

olamendy-rest-f6 

图 6:代码编辑器

下一步是将此功能公开为 Web 服务,即选择 File | New 选项并导航至 Categories 树中的 Web Services 节点。然后选择 Java Web Service 选项(参见图 7)。

olamendy-rest-f7 

图 7:将功能公开为 Web 服务

单击 OK 按钮。将显示 Create Java Web Service 向导。然后单击 Next 按钮。

在 Select Deployment Platform 页面中,选择 Java EE 1.5, with support for JAX-WS Annotations 选项,然后单击 Next 按钮(参见图 8)。

olamendy-rest-f8 

图 8:启用 JAX-WS 批注支持

在 Generation Options 页面中,选择要发布的组件(本例中为 EchoServiceImpl 类),并在 Web Service NamePort Name 域中分别输入信息。单击 Next 按钮(图 9)。

olamendy-rest-f9 

图 9:指定 Web Service Name 和 Port Name

在 Message Format 页面中,选择 SOAP 1.1 BindingDocument/Wrapped 选项作为 SOAP Message Format,以便与其他 Web 服务实现进行互操作。

Oracle WebLogic Server 的 Web 服务容器支持 SOAP 1.1/1.2。我们将使用 SOAP 1.1,因为 WS-I Basic Profile 1.1 强制要求将其作为互操作的主要机制。单击 Next 按钮(参见图 10)。

olamendy-rest-f10 

图 10:选择 SOAP 1.1 Binding 和 Document/Wrapped 选项

在 Methods 页面中,(从 Available Methods 下拉列表中)选择 Java Web 服务将公开的方法的列表。在本例中,只有一个方法要公开。单击 Next 按钮(参见图 11)。

olamendy-rest-f11 

图 11:公开方法

在 Additional Classes 页面中,有一个选择辅助类的选项。在本例中,无需任何辅助类。单击 Next 按钮(图 12)。

olamendy-rest-f12 

图 12:选择 Additional Classes

在开发安全 Web 服务的过程中,Configure Policies 页面是非常重要的一步。为定义 Web 服务的安全策略,与 WS-Security 标准一起引入了 WS-Policy 和 WS-SecurityPolicy 标准。

WS-Policy 规范定义了一种框架,允许 Web 服务宣传其策略,并允许客户端指定其策略要求。WS-SecurityPolicy 是 WS-Policy 的扩展,特别用于定义安全策略断言。这些断言(均符合 WS-Policy 和 WS-SecurityPolicy 模式)告诉 Oracle WebLogic Server 如何实施对 Web 服务的保护。它们还告诉客户端需要如何调用 Web 服务。在 Oracle WebLogic Server 中,这些断言还用于定义传输级策略和消息级对等策略。

可以使用 Configure Policies 页面附加一个或多个策略文件以配置安全性。第一个选项是在 OWSM Policies、WLS Policies 和 No Policies 之间进行选择。Oracle WebLogic Server 提供了许多预定义策略,这些策略反映了 Web 服务安全性以及与其他 Web 服务供应商的互操作性的常见使用情况。

OWSM 和 WLS 策略是一种用于在整个组织内一致管理和保护 Web 服务的策略框架。这两种框架都使用 WS-Policy 标准构建。

对于每个 OWSM 策略,均可以有一个对等的 WLS 策略。使用策略驱动的安全性的主要优点在于此模型将安全考虑与应用程序逻辑隔离开来,因此开发人员可以专注于构建应用程序以满足功能要求,而将安全方面委托给框架来实现。

为实现与使用 WCF 4.0 的 Microsoft.NET 解决方案的互操作性,需要选择 Oracle WebLogic Server 所支持的相应策略。根据 Oracle 和 Microsoft 关于实现双方平台之间互操作性的共同努力,推荐的最佳选项是 OWSM 策略 oracle/wss11_username_token_with_message_protection_service_policy(参见图 13)。

此策略根据 WS-Security 1.1 标准对入站 SOAP 请求执行消息级保护(即消息完整性和消息机密性)和身份验证。

通过 WS-Security 的基本 128 对称密钥技术套件来保护消息,尤其是用于消息机密性的 RSA 密钥机制、用于消息完整性的 SHA-1 哈希算法以及 AES-128 位加密。

通过安全性配置来配置密钥库。通过 UsernameToken WS-Security SOAP 头提供凭证。支持明文和摘要机制。根据配置的身份库对凭证进行身份验证。

还可以通过相对应的 WLS 策略(如 Wssp1.2-2007-Wss1.1-UsernameToken-Plain-EncryptedKey-Basic128.xml、Wssp1.2-2007-SignBody.xml 和 Wssp1.2-2007-EncryptBody.xml)的列表来指定此 OWSM 策略。

基本上,OWSM 策略 oracle/wss11_username_token_with_message_protection_service_policy(参见图 13)允许 Web 服务用户插入用户名和口令凭证,允许使用带公共密钥/私钥组合的公共密钥基础架构 (PKI) 对传出 SOAP 消息进行签名和加密。

然后 Web 服务进行解密并验证凭证和消息的完整性。我们可以对端点或端口应用策略。因此可以有多个端口,每个端口有自己的相应策略。

olamendy-rest-f13

图 13:配置策略

单击 Next 按钮,在 Provide Handler Details 页面中,单击 Finish 按钮结束向导(因为我们不需要对服务进行任何配置)。

向导关闭后,就可以看到已经使用 @WebService 和 @SecurityPolicy 批注装饰了 EchoServiceImpl 类(参见图 14)。

olamendy-rest-f14 

图 14:用批注装饰的类

还可以看到 web.xml 文件中添加了从 Web 容器访问服务所必需的配置。我们的 Web 服务现在公开为一个 servlet,该 servlet 映射到 URL /EchoServiceImplPort。还可以针对我们的情形对此文件进行定制(参见图 15)。

olamendy-rest-f15 

图 15:定制 XML 文件

为了与 OWSM 策略一起工作,oracle/wss11_username_token_with_message_protection_service_policy 使用公共密钥基础架构,因此需要为应用服务器配置密钥库和凭证库。

密钥库和凭证库的默认路径配置是目录 WL_DOMAIN/config/fmwconfig 下的 jps-config.xml 文件,其中 WL_DOMAIN 变量是 Oracle WebLogic Server 域的目录。默认凭证库指向 cwallet.sso 文件,默认密钥库指向 default-keystore.jks 文件。这两个文件都在目录 WL_DOMAIN/config/fmwconfig 下。

如果 WL_DOMAIN/config/fmwconfig 目录下没有 default-keystore.jks 文件,将使用 Java Development Kit (JDK) 安装中的 keytool 命令创建该库和 Web 服务的自签名证书,如清单 1 所示。

您可以看到,在本例中,我们已经使用 RSA 算法为主要 weblogic 定义了公共密钥/私钥组合。您可以根据自己的业务情形调整这些参数。

keytool -genkey -alias weblogic –dname “cn=weblogic” -keyalg RSA -keystore default-keystore.jks
-keypass weblogic1 -storepass weblogic1


清单 1:创建库和证书
 

现在,将 Web 服务的公共密钥和规范名称(存储在 default-keystore.jks 文件中)导出到 X.509 证书中,以便客户端解决方案使用,如清单 2 所示。

keytool -export -alias weblogic -file c:\temp\weblogic.cer -keystore
default-keystore.jks -storepass weblogic1
 

清单 2:导出公共密钥和规范名称

下一步是使用 EM 界面中的 WLST 命令(位于 MW_HOME/common/bin 中,其中 MW_HOME 变量是 Oracle 中间件目录)向凭证库添加密钥库口令(在本例中,default-keystore.jks 的口令为 weblogic1)。

为了使用 WLST 命令,需要连接到 Oracle WebLogic Server 域。为此,右键单击 Application Navigator 窗口中的 EchoServiceImpl.java 文件,启动 Oracle JDeveloper 中内嵌的 Oracle WebLogic Server 中的默认域。然后从上下文菜单中选择 Run 选项(参见图 16)。

olamendy-rest-f16 

图 16:启动默认域

现在执行 MW_HOME/oracle_common/common/bin/wlst.cmd 以及清单 3 所示的命令,这些命令位于 WebLogic Scripting Tool (WSLT) 环境下。您可以根据自己的业务情形调整这些命令,将 user weblogic 和 password weblogic1 替换为自己的配置。

connect()
createCred(map="oracle.wsm.security", key="keystore-csf-key", user="keystore",password="weblogic1")
createCred(map="oracle.wsm.security", key="sign-csf-key", user="weblogic", password="weblogic1")
createCred(map="oracle.wsm.security", key="enc-csf-key", user="weblogic", password="weblogic1")


清单 3:在 WSLT 环境中执行命令

现在,我们已成功开发和配置了解决方案的服务器端。

可以在通过 URL 访问 Web 服务描述语言 (WSDL),如图 17 所示: http://localhost:7101/SecureWebServiceInterAppl-EchoServiceProj-context-root/EchoServiceImplPort?wsdl.

olamendy-rest-f17 

图 17:Web 服务描述语言

建议您在使用 WCF 工具生成客户端构件(如下节所示)时禁用 @SecurityPolicy 批注,稍后再重新启用 @SecurityPolicy 批注以支持安全性。之所以这样建议,是因为 WCF 框架对安全性限制的理解不很好。

还可以选择创建和发布 WSDL 文档,避免直接使用 URL http://localhost:7101/SecureWebServiceInterAppl-EchoServiceProj-context-root/EchoServiceImplPort?wsdl。

开发解决方案的客户端

为了创建解决方案的客户端,请打开 Microsoft Visual Studio.NET 2010。然后选择 File | New | Project 菜单选项,在 Installed Templates 树中导航至 Visual C# | Windows 节点,并选择 Console Application。然后输入项目的描述性名称、解决方案以及将要存储基础文件的目录(参见图 18)。

olamendy-rest-f18 

图 18:输入项目名称、解决方案和目录

下一步是添加对构建 WCF 解决方案所必需的组合件的引用。在 Solution Explorer 窗口中,右键单击客户端应用程序中的 References 节点。然后从上下文菜单中选择 Add Reference 选项。在 Add Reference 窗口中,选择 System.Runtime.SerializationSystem.ServiceModel 组合件(参见图 19)。

olamendy-rest-f19 

图 19:选择 System.Runtime.Serialization 和 System.ServiceModel 组合件

为了调用 Web 服务的操作,需要将服务合约(由 WSDL 文档表示)导入客户端的原生表示。调用 Web 服务操作的最常见方式是使用代理。

代理是一个类,它将代表 Web 服务合约的接口公开。如果 Web 服务公开多个合约(在许多端点上),则需要为每个合约创建一个代理。代理提供与服务相同的接口,并实现将消息从客户端传输到服务器端的通信机制。

一种生成 WCF 代理的方式是使用 SvcUtil.exe 命令,将 WSDL 文档或消息交换地址 (MEX) 作为参数传递,如清单 4 和图 20 所示。

svcutil.exe  http://localhost:7101/SecureWebServiceInterAppl-EchoServiceProj-context-root/EchoServiceImplPort?wsdl


清单 4:运行 SvcUtil.exe 命令

olamendy-rest-f20 

图 20:运行 SvcUtil.exe 命令的输出

这一操作后,我们已经使用 WCF 构件将基础通信机制与 Web 服务封装在一起。

现在需要使用 Microsoft Management Console (MMC) 将清单 2 代码生成的 X.509 证书导入 Windows Certificates 中的 CurrentUser-Certificates/Personal/Certificates 中,如图 21 所示。

olamendy-rest-f21 

图 21:导入 X.509 证书

下一步是向项目添加配置文件(app.config 文件),其中包含使用安全机制启动 Web 服务所需的信息(参见清单 5)。

要配置的第一个元素是端点元素(清单 5 中的粉红色部分)。端点包含三个基本元素:表示服务所在位置的 address、定义服务执行哪些任务的 contract,以及定义如何与服务通信的 binding。端点可以包含名为 behavior 的可选元素,用于设置某些环境配置。

在本例中,对绑定(通过端点元素的 binding 和 bindingConfiguration 属性)进行了定制,以支持 WS-Security 以及与 Oracle WebLogic Server 的互操作性的主要要求(清单 5 中的绿色部分)。

最后,行为(通过端点元素的 behavior 和 behaviorConfiguration 属性)告诉如何查找 X.509 证书(清单 5 中的橙色部分)。

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.serviceModel>
    <behaviors>
      <endpointBehaviors>
        <behavior name="EchoServiceImplPortBehavior">
          <clientCredentials>
            <serviceCertificate>
              <defaultCertificate findValue="weblogic" storeLocation="CurrentUser" storeName="My" x509FindType="FindBySubjectName"/>
            </serviceCertificate>
          </clientCredentials>
        </behavior>
      </endpointBehaviors>
    </behaviors>

    <bindings>
      <customBinding>
        <binding name="EchoServiceImplPortBinding">
          <security defaultAlgorithmSuite="Basic128"
                    authenticationMode="UserNameForCertificate"
                    requireDerivedKeys="false" securityHeaderLayout="Lax"
                    includeTimestamp="true"
                    keyEntropyMode="CombinedEntropy"
                    messageProtectionOrder="SignBeforeEncrypt"
                    messageSecurityVersion="WSSecurity11WSTrustFebruary2005WSSecureConversation
February2005WSSecurityPolicy11BasicSecurityProfile10"
                    requireSignatureConfirmation="true">
            <localClientSettings
                  cacheCookies="true"
                  detectReplays="false"
                  replayCacheSize="900000"
                  maxClockSkew="00:05:00"
                  replayWindow="00:05:00"
                  sessionKeyRenewalInterval="10:00:00"
                  sessionKeyRolloverInterval="00:05:00"
                  reconnectTransportOnFailure="true"
                  timestampValidityDuration="00:05:00"
                  cookieRenewalThresholdPercentage="60" />
            <localServiceSettings
                  detectReplays="true"
                  issuedCookieLifetime="10:00:00"
                  maxStatefulNegotiations="128"
                  replayCacheSize="900000"
                  maxClockSkew="00:05:00"
                  negotiationTimeout="00:01:00"
                  replayWindow="00:05:00"
                  inactivityTimeout="00:02:00"
                  sessionKeyRenewalInterval="15:00:00"
                  sessionKeyRolloverInterval="00:05:00"
                  reconnectTransportOnFailure="true"
                  maxPendingSessions="128"
                  maxCachedCookies="1000"
                  timestampValidityDuration="00:05:00" />
            <secureConversationBootstrap />
          </security>
          <textMessageEncoding
              maxReadPoolSize="64"
              maxWritePoolSize="16"
              messageVersion="Soap11"
              writeEncoding="utf-8">
            <readerQuotas
                maxDepth="32"
                maxStringContentLength="8192"
                maxArrayLength="16384"
                maxBytesPerRead="4096"
                maxNameTableCharCount="16384" />
          </textMessageEncoding>
          <httpTransport
              manualAddressing="false"
              maxBufferPoolSize="524288"
              maxReceivedMessageSize="65536"
              allowCookies="false"
              authenticationScheme="Anonymous"
              bypassProxyOnLocal="false"
              hostNameComparisonMode="StrongWildcard"
              keepAliveEnabled="true"
              maxBufferSize="65536"
              proxyAuthenticationScheme="Anonymous"
              realm=""
              transferMode="Buffered"
              unsafeConnectionNtlmAuthentication="false"
              useDefaultWebProxy="true"
          />
        </binding>
      </customBinding>
    </bindings>

    <client>
      <endpoint address="http://localhost:7101/SecureWebServiceInterAppl-EchoServiceProj-context-root/EchoServiceImplPort"
                binding="customBinding" bindingConfiguration="EchoServiceImplPortBinding"
                contract="EchoServiceImpl" name="EchoServiceImplPort" behaviorConfiguration="EchoServiceImplPortBehavior">
        <identity>
          <dns value="weblogic"/>
        </identity>
      </endpoint>

    </client>
  </system.serviceModel>
</configuration>

清单 5:添加配置文件

为了使用代理,首先需要创建一个实例(清单 6 中的橙色部分)并通过此实例调用 Web 服务的操作(清单 6 中的绿色部分)。因为我们使用的安全策略要求通过 UsernameToken WS-Security SOAP 头传递凭证,所以需要使用代理属性设置凭证(清单 6 中的蓝色部分)。

Using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace EchoSvcClientConsAppl
{
    class Program
    {
        static void Main(string[] args)
        {
            EchoServiceImplClient clientProxy = new EchoServiceImplClient();
            clientProxy.ClientCredentials.UserName.UserName = "weblogic";
            clientProxy.ClientCredentials.UserName.Password = "weblogic1";

           
string output = clientProxy.Echo("Hello world!");             System.Console.WriteLine("The EchoService output is '"+output+"'");             System.Console.ReadLine();         }     } }


清单 6:创建实例并调用 Web 服务的操作

最后,在客户端运行该控制台应用程序。输出如图 22 所示。

olamendy-rest-f22 

图 22:运行控制台应用程序的输出

总结

本文说明了如何使用 WS-* 标准、Oracle 技术(如 Oracle JDeveloper 11g 和 Oracle WebLogic Server)创建安全的 Web 服务,以及如何使用 Microsoft 技术(如 Visual Studio.NET 2010、Microsoft.NET 4.0 Framework 和 Windows Communication Foundation 4.0)来使用 Web 服务。

利用此方法,您可以根据自己的业务情形调整本文中所介绍的示例用例,构建强健的企业级解决方案。



Juan Carlos (John Charles) Olamendy Turruellas
[johnx_olam@fastmail.fm] 是一位高级集成解决方案架构师、开发人员和顾问。他的主要工作是面向对象的分析和设计、数据库设计和重构、使用设计模式的企业应用程序架构和集成,以及软件开发流程管理。他曾多次获得 Microsoft 最具价值专家 (MVP) 称号,是一位 Oracle ACE。