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

作者:Juan Carlos (John Charles) Olamendy Turruellas

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

2011 年 5 月发布

第 1 部分中,我介绍了在 Oracle WebLogic Server 11g 中运行一个服务而客户端通过 Microsoft WCF 4.0 技术使用此服务的情形。本文是该系列文章的第 2 部分,将介绍在 Microsoft WCF 4.0 平台中运行一个服务,并使用一个服务代理使用此服务的情形,其中,该服务代理是使用 Oracle ADF 技术开发的,并且运行在 Oracle WebLogic Server 中。

在企业解决方案中,安全性是保证成功采用面向服务的架构 (SOA) 的一项基本要求。业界主要厂商和标准机构扩展了 SOAP 协议以产生一系列安全规范(称为 WS-* 协议),为 Web 服务提供端到端的安全性,诸如消息机密性和完整性、令牌交换(SAML、用户名/口令、Kerberos 和 X.509)、信任和联合。

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

许多组织中常见的一种互操作情形,是让企业应用程序运行在 Microsoft.NET 平台中并使用 Web 服务方法(支持 WS-* 协议体系)公开应用程序功能,让客户端运行在 Oracle WebLogic Server 环境中并以安全方式使用这些功能。在本文中,我将讨论如何使用 Oracle 和 Microsoft 的新兴技术(尤其是使用 Oracle WebLogic Server 11g 和 Microsoft Windows Communication Foundation 4.0)支持以上情形。

Microsoft Windows Communication Foundation (WCF) 是在 Microsoft.NET 环境中实现分布式解决方案的基础架构。在此编程模型中,WS-* 协议体系被抽象为一组称为协议通道的构件。协议通道正好在使用特定传输协议通道发送和接收消息之前和之后执行。Oracle WebLogic Server 11g 是用于开发、部署和托管 J2EE 解决方案(尤其是使用 JAX-WS 标准的 Web 服务)的最全面的应用服务器。

通过使用 WCF 和 WebLogic Server 技术,我们可以支持企业环境中的多种安全情形。在这些情形中,有一种是使用公共密钥基础架构 (PKI) 在基础子系统之间提供安全通道,由此在分布式系统中进行消息交换。

为了支持该业务案例,解决方案必须通过使用 WS-Security 相关协议来支持消息级安全,这些协议为身份验证、消息保护和完整性以及高级互操作性提供了基础。本文将重点讨论如何为 WCF 4.0 环境中托管的 Web 服务定义 WS-Security 配置(使用 X.509 证书作为安全机制),该服务可以与 Oracle WebLogic Server 11g 环境中运行的客户端进行互操作。

开发解决方案的服务器端

为简化问题并集中讨论使用安全 Web 服务标准的集成解决方案,我们的服务将非常简单,只是执行 之类的基本算术运算。在实际 Web 服务解决方案中,服务的实现将更为复杂,包括对向外界公开功能的业务对象的调用。

现在开始进行开发,首先打开 Visual Studio.NET 2010 IDE,创建一个新的应用程序和基础解决方案。输入应用程序和解决方案的名称以及工作目录,最后单击 OK

oracle-msft-interoperate-part2-f1

下一步是添加 Web 服务合约,定义服务端点的操作和用于传入和传出消息的数据模式。在 WCF 4.0 中,通过 C# 接口语法(声明服务端点的意图而不指定实际行为)并使用 ServiceContact 和 OperationContract 属性对该接口进行批注来实现该合约,如下所示。请注意,我们覆盖了默认的服务命名空间 (http://tempuri.org),因为此结构旨在组合多个服务时唯一标识服务并消除歧义。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;

namespace MathServiceConsAppl
{
    [ServiceContract(Namespace="http://www.mathservices.com/services/2010/12")]
    public interface IMathService
    {
        [OperationContract]
        int Add(int param1, int param2);

        [OperationContract]
        int Substract(int param1, int param2);
    }
}

下一步是为项目添加 Class 构件以支持服务端点。请记住,我们将直接在服务端点实现行为,但这并非最佳做法,因为端点从根本上是业务逻辑的访问点,而业务逻辑实际上是在应用程序的业务对象中或业务逻辑层中实现的:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;

namespace MathServiceConsAppl
{
    public class MathServiceImpl : IMathService
    {
        public int Add(int param1, int param2)
        {
            return param1 + param2;
        }

        public int Substract(int param1, int param2)
        {
            return param1 - param2;
        }
    }
}

现在添加主要执行点以定义托管环境(通过 ServiceHost 类的实例,将引用传递至服务端点类),该环境负责管理运行的服务的生命周期和上下文。虽然为了简化,我们将在控制台应用程序中托管 Web 服务,但在实际解决方案中,托管环境是 Internet Information Services (IIS)、Windows 服务和 Windows 进程激活服务 (WAS) 基础架构中的一个进程。

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

namespace MathServiceConsAppl
{
    class Program
    {
        static void Main(string[] args)
        {
            using (ServiceHost host = new ServiceHost
                    (typeof(MathServiceConsAppl.MathServiceImpl)))
            {
                try
                {
                    host.Open();
                    System.Console.WriteLine("Press any key to finish ...");
                    System.Console.ReadKey();
                }
                catch (Exception ex)
                {
                    System.Console.WriteLine("Exception: "+ex.Message);
                }
                finally
                {
                    host.Close();
                }
            }
        }
    }
}

为了以可互操作方式支持使用 WS-Security 相关标准的基于消息的安全性,需要使用 X.509 证书配置基础架构。在本文中,将通过 makecert.exe 命令使用测试证书。–pe 开关使得可以导出私钥。–n 开关定义与服务主机的域(或计算机)名称相匹配的证书的通用名称,它用于身份验证。–sv 开关定义私钥文件。–sky 开关可以交换或数字签名。

 oracle-msft-interoperate-part2-f2

然后,需要将私钥和公钥组合成一个文件。.cer 文件包含公钥,.pvk 包含私钥,.pfx 文件是包含公钥和私钥的交换文件。请记住,以此方式产生的证书不应用于生产环境,因此您应向受信任的第三方证书颁发机构请求证书。

 oracle-msft-interoperate-part2-f3

现在,需要在托管计算机中将 localhost.pfx 导入 LocalMachine/Personal 证书库。这使得服务器能够使用公钥对加密消息进行解密,使用私钥对消息进行加密。

oracle-msft-interoperate-part2-f4   


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

1   <?xml version="1.0" encoding="utf-8" ?>
2   <configuration>
3     <system.serviceModel>
4           <behaviors>
5               <serviceBehaviors>
6                   <behavior name="MathServiceConsAppl.MathServiceImplBehavior">
7                       <serviceMetadata httpGetEnabled="true" />
8                      <serviceDebug includeExceptionDetailInFaults="false" />
9                        <serviceCredentials>
10                         <clientCertificate>
11                           <authentication certificateValidationMode="PeerOrChainTrust"/>
12                         </clientCertificate>
13                         <serviceCertificate
14                           findValue="localhost"
15                          storeLocation="LocalMachine"
16                           storeName="My"
17                           x509FindType="FindBySubjectName"/>
18                    </serviceCredentials>
19                   </behavior>
20               </serviceBehaviors>
21           </behaviors>
22           <bindings>
23             <customBinding>
24               <binding name="MathServiceConsAppl.MathServiceImplBinding">
25                 <security defaultAlgorithmSuite="Basic128"
26                           authenticationMode="UserNameForCertificate"
27                           requireDerivedKeys="false" securityHeaderLayout="Lax"
28                           includeTimestamp="true"
29                           keyEntropyMode="CombinedEntropy"
30                           messageProtectionOrder="SignBeforeEncrypt"
31                           messageSecurityVersion="WSSecurity11WSTrustFebruary2005WSSecureConversation
32                                    February2005WSSecurityPolicy11BasicSecurityProfile10"
33                           requireSignatureConfirmation="true">
34                   <localClientSettings
35                         cacheCookies="true"
36                         detectReplays="false"
37                         replayCacheSize="900000"
38                         maxClockSkew="00:05:00"
39                         replayWindow="00:05:00"
40                         sessionKeyRenewalInterval="10:00:00"
41                         sessionKeyRolloverInterval="00:05:00"
42                         reconnectTransportOnFailure="true"
43                         timestampValidityDuration="00:05:00"
44                         cookieRenewalThresholdPercentage="60" />
45                   <localServiceSettings
46                         detectReplays="true"
47                         issuedCookieLifetime="10:00:00"
48                         maxStatefulNegotiations="128"
49                         replayCacheSize="900000"
50                         maxClockSkew="00:05:00"
51                         negotiationTimeout="00:01:00"
52                         replayWindow="00:05:00"
53                         inactivityTimeout="00:02:00"
54                         sessionKeyRenewalInterval="15:00:00"
55                         sessionKeyRolloverInterval="00:05:00"
56                         reconnectTransportOnFailure="true"
57                         maxPendingSessions="128"
58                         maxCachedCookies="1000"
59                         timestampValidityDuration="00:05:00" />
60                   <secureConversationBootstrap />
61                 </security>
62                 <textMessageEncoding
63                     maxReadPoolSize="64"
64                     maxWritePoolSize="16"
65                     messageVersion="Soap12"
66                     writeEncoding="utf-8">
67                   <readerQuotas
68                       maxDepth="32"
69                       maxStringContentLength="8192"
70                       maxArrayLength="16384"
71                       maxBytesPerRead="4096"
72                       maxNameTableCharCount="16384" />
73                 </textMessageEncoding>
74                 <httpTransport
75                     manualAddressing="false"
76                     maxBufferPoolSize="524288"
77                     maxReceivedMessageSize="65536"
78                     allowCookies="false"
79                     authenticationScheme="Anonymous"
80                     bypassProxyOnLocal="false"
81                     hostNameComparisonMode="StrongWildcard"
82                     keepAliveEnabled="true"
83                     maxBufferSize="65536"
84                     proxyAuthenticationScheme="Anonymous"
85                     realm=""
86                     transferMode="Buffered"
87                     unsafeConnectionNtlmAuthentication="false"
88                     useDefaultWebProxy="true" />
89               </binding>
90             </customBinding>
91           </bindings>
92   <services>
93               <service behaviorConfiguration="MathServiceConsAppl.MathServiceImplBehavior" 
94                  name="MathServiceConsAppl.MathServiceImpl">
95                   <endpoint address="" binding="customBinding" bindingConfiguration=
96                      "MathServiceConsAppl.MathServiceImplBinding" contract="MathServiceConsAppl.IMathService">
97                       <identity>
98                           <dns value="localhost" />
99                       </identity>
100                   </endpoint>
101                  <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
102                   <host>
103                       <baseAddresses>
104                           <add baseAddress="http://localhost:8080/MathService" />
105                      </baseAddresses>
106                   </host>
107               </service>
108           </services>
109       </system.serviceModel>
110   </configuration>


第一个要配置的元素是服务端点:在本例中,一个提供数学服务,另一个用于访问服务的元数据(第 93-107 行)。端点包含三个基本元素:服务所监听的地址、定义服务提供哪些功能的合约,以及定义如何与服务通信的绑定。端点可以包含名为 behavior 的可选元素,用于设置某些环境配置。服务端点中值得重点注意的是标识元素,它指示 WCF 平台期待服务证书的规范名称与服务主机的域或计算机名相匹配。另一个重要方面是主机元素,它设置所有服务的基地址。

依据产品文档,对绑定(通过端点元素的 binding 和 bindingConfiguration 属性)进行了定制,以支持 WS-Security 以及与 Oracle WebLogic Server 的互操作性的主要要求(第 22-91 行)。

最后,行为(通过端点元素的 behavior 和 behaviorConfiguration 属性)告诉如何查找 X.509 证书(第 4-21 行)。

开发解决方案的客户端

为创建客户端解决方案,请打开 Oracle JDeveloper 11g IDE 并创建一个新应用程序。

 oracle-msft-interoperate-part2-f5

单击 OK 按钮。输入应用程序名称、默认程序包和工作目录。

oracle-msft-interoperate-part2-f6

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

oracle-msft-interoperate-part2-f7

单击 Next 按钮,转至 Create Java Settings 页面检查项目结构。最后,单击 Finish 按钮。

 

oracle-msft-interoperate-part2-f8 

解决方案的下一步是创建 JAX-WS 服务客户端。JAX-WS 规范是一种编程模型,它允许通过使用 POJO 类和 Java 批注装饰基础 POJO 类的行为来实现和使用 Web 服务。

创建 JAX-WS 服务客户端的第一步是通过选择 File | New 选项并单击 OK 按钮来添加 Web 服务数据控件。Oracle ADF 11g 中的 Web 服务数据控件提供了一种简单、方便的编程模型,用于在 ADF 应用程序中合并 web 服务。数据控件提取与 Web 服务的通信,由此为 ADF 页面提供一致的数据访问机制。

  oracle-msft-interoperate-part2-f9


单击 OK 按钮,打开 Create Web Service Data Control 向导。在第一页中,输入构件的名称以及描述服务元数据的 WSDL 的 URL。

  oracle-msft-interoperate-part2-f10


单击 Next 按钮,Data Control Operations 页面允许选择要从 Web 服务使用的功能。在本例中,我们将使用可用服务端点中的所有操作。

  oracle-msft-interoperate-part2-f11


单击 Next 按钮,在 Response Format 页面中,可以指定消息交换的格式。

  oracle-msft-interoperate-part2-f12


单击 Finish 按钮完成 Web 服务数据控件的创建。下一步是配置新建的数据控件,为此,右键单击控件并从上下文菜单中选择 Define Web Service Security 选项。

  oracle-msft-interoperate-part2-f13


出现 Edit Data Control Policies 窗口时,需要选择 oracle/wss11_username_token_with_message_protection_client_policy 策略,它允许客户端向 web 服务传播固定标识,同时向整个 SOAP 消息提供安全性。这是允许与 WCF 4.0 解决方案互操作的策略(依据 WebLogic 文档)。

oracle-msft-interoperate-part2-f14


此策略根据 WS-Security 1.1 标准对入站 SOAP 请求执行消息级保护和身份验证。通过 WS-Security 的基本 128 对称密钥技术套件来保护消息,尤其是用于消息机密性的 RSA 密钥机制、用于消息完整性的 SHA-1 哈希算法以及 AES-128 位加密。通过 UsernameToken WS-Security SOAP 头提供凭证。支持明文和摘要机制。默认配置是 WL_DOMAIN/config/fmwconfig 目录下的 jps-config.xml 文件,其中 WL_DOMAIN 变量是 WebLogic Server 域的目录。默认凭证库指向 cwallet.sso 文件,默认密钥库指向 default-keystore.jks。

然后单击 Override Properties 按钮,以便为 csf-key 和 keystore.recipient.alias 元素提供配置。

  oracle-msft-interoperate-part2-f15


csf-key 属性定义凭证库中用于保存在 UsernameToken 头中向 web 服务传播的用户的密钥。此属性用于向 Web 服务进行身份验证,而不必实际在策略中嵌入用户名和口令。

为了在运行时创建该属性,需要在客户端 WLS 域中执行如下所示的 WLST 命令。EM 界面中的 WLST 命令位于 MW_HOME/oracle_common/common/bin 中,其中,MW_HOME 变量是 Oracle 中间件目录。为了使用 WLST 命令,需要连接到 WebLogic Server 域。


createCred(map="oracle.wsm.security",key="mathclient-key",user="username",password="password")


为了在开发时创建此属性,需要单击 New Key 按钮定义密钥,并在部署应用程序时将此配置压入 WLS 域凭证库。

  

oracle-msft-interoperate-part2-f16


keystore.recipient.alias 属性是 WLS 客户端域的密钥库中对服务器端 X.509 证书的别名的引用。为了将服务器端 X.509 证书作为密钥库中的受信任证书导入,应执行 keytool 命令:

keytool –import –trustcacerts –alias localhost   –file localhost.cer –keystore default-keystore.jks

为了使应用程序服务器能够访问 default-keystore.jks 密钥库,请在客户端 WLS 域中执行以下 WLST 命令。

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

下一步是配置用户界面以便与 Web 服务交互。请打开 faces-config.xml 文件来配置页面和基础页面流。将一个 JSF 页面从 JSF Diagram Objects 拖放到 faces-config 设计器上。

oracle-msft-interoperate-part2-f17

最后,双击 index.jsp 页面创建该页面,并从 DataControl 菜单拖放操作。

oracle-msft-interoperate-part2-f18

现在可以开始在实际情形中测试应用程序了。

 

oracle-msft-interoperate-part2-f19 

通过阅读本文,您已经了解如何使用 WS-* 标准和 Microsoft 技术创建安全的 Web 服务,以及如何利用 Oracle 技术来使用 Web 服务,您可以根据自己的业务情形对此示例用例进行修改,以使用此方法构建强健的企业级解决方案。


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