内容


Java Web 服务

Axis2 WS-Security 基础

将 Rampart 安装到 Axis2 中并实现 UsernameToken 处理

系列内容:

此内容是该系列 # 部分中的第 # 部分: Java Web 服务

敬请期待该系列的后续内容。

此内容是该系列的一部分:Java Web 服务

敬请期待该系列的后续内容。

安全性对于众多企业服务来说都是一个重要需求。并且,尝试实现自己的安全性也颇具风险,因为任何微小的疏忽都会导致严重的安全漏洞。这些特征引起了人们对安全处理标准化的兴趣,许多专家为标准作出贡献并避免出现任何个人的疏漏。基于 SOAP 的 Web 服务可以使用广泛支持的 WS-Security 和相关标准来满足它们的安全需求,允许针对每种服务相应地配置安全性。

Apache Axis2 通过 Rampart 模块支持这些安全标准(见 参考资料)。在本文中,您将看到如何为 Axis2 安装、配置和使用 Rampart 以实现基本的安全功能,即在一个服务请求中发送用户名和密码。在本系列的后续文章中,您将理解如何使用 Rampart 实现更加复杂的安全性。

WS-Security

WS-Security 是向 SOAP Web 服务消息交换添加安全性的一种标准(见 参考资料)。它使用一个 SOAP 消息头部元素将安全信息附加到消息中,使用令牌(token)的形式传递不同类型的声明(包括名称、身份、密匙、组、特权、功能等等)以及加密和数字签名信息。WS-Security 支持多种形式的令牌、多个可信任域、多种签名格式和多种加密技术,因此大多数情况下头部信息需要针对每种内容包含特定的格式和算法识别。附加的信息会导致头部信息产生复杂的结构,如 清单 1 所示(进行了大量编辑)— 包含签名和加密的样例消息:

清单 1. 包含签名和加密的样例消息
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" ...>
 <soap:Header>
  <wsse:Security soap:mustUnderstand="1">
   <wsu:Timestamp wsu:Id="Timestamp-d2e3c4aa-da82-4138-973d-66b596d66b2f">
    <wsu:Created>2006-07-11T21:59:32Z</wsu:Created>
    <wsu:Expires>2006-07-12T06:19:32Z</wsu:Expires>
   </wsu:Timestamp>
   <wsse:BinarySecurityToken ValueType="...-x509-token-profile-1.0#X509v3"
     EncodingType="...-wss-soap-message-security-1.0#Base64Binary"
     xmlns:wsu="...oasis-200401-wss-wssecurity-utility-1.0.xsd"
     wsu:Id="SecurityToken-faa295...">MIIEC56MQswCQY...</wsse:BinarySecurityToken>
   <xenc:EncryptedKey xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
    <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
    <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
     <wsse:SecurityTokenReference>
      <wsse:KeyIdentifier ValueType=
       "...#X509SubjectKeyIdentifier">LlYsHyhNnOVA9Aj7...</wsse:KeyIdentifier>
     </wsse:SecurityTokenReference>
    </KeyInfo>
    <xenc:CipherData>
     <xenc:CipherValue>g+A2WJhsoGBKUydZ9Za...</xenc:CipherValue>
    </xenc:CipherData>
    <xenc:ReferenceList>
     <xenc:DataReference URI="#EncryptedContent-ba0556c3-d443-4f34-bcd1-14cbc32cd689" />
    </xenc:ReferenceList>
   </xenc:EncryptedKey>
   <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
    <SignedInfo>
     <ds:CanonicalizationMethod
       Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"
       xmlns:ds="http://www.w3.org/2000/09/xmldsig#" />
     <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
     <Reference URI="#Id-c80f735c-62e9-4001-8094-702a4605e429">
      <Transforms>
       <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
      </Transforms>
      <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
      <DigestValue>lKjc5nyLQDZAIu/hZb4B6mLquow=</DigestValue>
     </Reference>
     ...
    </SignedInfo>
    <SignatureValue>TiLmWvlz3mswinLVQn58BgYS0368...</SignatureValue>
    <KeyInfo>
     <wsse:SecurityTokenReference>
      <wsse:Reference URI="#SecurityToken-faa295..."
        ValueType="...-x509-token-profile-1.0#X509v3" />
     </wsse:SecurityTokenReference>
    </KeyInfo>
   </Signature>
  </wsse:Security>
 </soap:Header>
 <soap:Body wsu:Id="Id-8db9ff44-7bef-4737-8091-cdac51a34db8">
  <xenc:EncryptedData Id="EncryptedContent-ba05..."
    Type="http://www.w3.org/2001/04/xmlenc#Content"
    xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
   <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc"/>
   <xenc:CipherData>
    <xenc:CipherValue>mirmi0KuFEEI56eu2U3cICz...</xenc:CipherValue>
   </xenc:CipherData>
  </xenc:EncryptedData>
 </soap:Body>
</soap:Envelope>

在本文中,您将看到一些 WS-Security 头部示例,并且只包含单个令牌。本系列下一篇文章将更进一步,探讨 清单 1 所示的复杂结构类型。

WS-Security 应用于实际的 SOAP 消息交换。服务实现可以检验 WS-Security 是否恰当应用于传入的消息,但是客户机需要提前知道它们必须实现什么才能使用服务。由于 WS-Security 的复杂性以及所支持的选项有限,因此仅使用文本描述很难实现这个目的,而手动配置 WS-Security 处理极容易出现错误。WS-Policy 是一个用于为 Web 服务指定扩展需求的通用结构,而 WS-SecurityPolicy 是一个专门针对 WS-Security 支持的 WS-Policy 扩展。这两者合起来可以以机器可读的方式描述 WS-Security 需求。WS-Policy 和 WS-SecurityPolicy 信息可以单独使用,也可以直接嵌入到 Web Services Description Language (WSDL) 文档内部,这样 Web 服务框架就可以将它们自动配置为服务的需求。

Rampart 简介

Rampart 是 Axis2 的安全模块,支持 WS-Security、WS-SecurityPolicy、WS-SecureConversation 和 WS-Trust。在本文中,您将只查看 Rampart 的 WS-Security 和 WS-SecurityPolicy 功能;后续文章将讨论其他特性。

由于 Rampart 是以模块形式实现(实际上是一对模块 — rampart.mar 和 rahas.mar),它将插入到 Axis2 处理框架中并在出站和入站处理的特定点拦截消息,检查对消息的修改或根据需要对消息作出修改,从而完成工作。

安装 Rampart

Rampart 附带了一些 .jar 文件(在发行版的 lib 目录中),以及一对 .mar 模块文件(位于 dist 目录中)。您必须将 .jar 文件添加到类路径中才能对 Axis2 使用 Rampart,并且必须将 .mar 文件添加到类路径或 Axis2 库结构中。

处理 Rampart .jar 和 .mar 文件的最简单方法是将它们添加到 Axis2 安装中。可以从 Rampart lib 目录中直接将 .jar 文件复制到 Axis2 lib 目录,从 Rampart dist 目录中直接将 .mar 文件复制到 Axis2 库/模块目录中。(还可以使用 Ant build.xml 在 Rampart 样例目录中将文件复制给 Axis2 安装。只需要将 AXIS2_HOME 环境变量设置为 Axis2 安装目录并从打开 Rampart 样例目录的控制台运行 ant)。

对于众多 WS-Security 特性,您还需要向 JVM 安全配置添加 Bouncy Castle 安全提供程序,向 Axis2 安装添加 Bouncy Castle .jar。本系列后续文章中涉及的其他安全特性(不包括本文将介绍的 UsernameToken)需要使用这一步骤。由于某些安全算法涉及专利问题,Bouncy Castle .jar 可以从 Rampart 独立下载(见 参考资料)。为您的 Java 运行时下载合适的 .jar 版本,并向 Axis2 lib 目录添加 .jar。您随后需要修改 Java 安装的安全策略来使用 Bouncy Castle 代码,需要向 Java 运行时的 lib/security 目录中的 java.security 文件添加一行代码。查找文件中具有不同 security.provider 行的部分,然后添加以下代码行:

security.provider.99=org.bouncycastle.jce.provider.BouncyCastleProvider

security.provider 行在文件中的顺序并不重要,但是最好将其添加到实现预定义安全提供程序的代码行的后面。

要在 Axis2 服务器安装中使用 Rampart 代码,需要创建一个新的 axis2.war 文件,其中包括新添加的 Rampart .jar 和 .mar 文件。可以使用 webapp 目录中提供的 Ant build.xml 来创建 axis2.war,假设您做了一处修改:删除文件末尾的 <exclude name="axis2-codegen*.jar"/> 行。然后在控制台中打开 Axis2 webapp 目录并运行 ant。运行完 build.xml 后,会发现创建的 axis2.war Web 应用程序出现在 Axis2 安装的 dist 目录中。

样例应用程序

示例代码中提供的应用程序(见 下载)基于我在 “Axis2 数据绑定” 中用于演示 Axis2 数据绑定方法的示例。对于本文以及有关 Axis2 WS-Security 支持的后续文章,我将这个示例精简到三个操作:getBookaddBookgetBooksByType。为了保持简单性,只提供了 Axis Data Binding (ADB) 版本的代码,但是这并不是在 Axis2 中使用 WS-Security 的必需要求 — Rampart 可以独立于代码使用的数据绑定技术实现 WS-Security,因此它可以处理 Axis2 支持的所有形式的数据绑定。

示例代码的根目录为 jws04code。在该目录内部,将找到 Ant build.xml 和 build.properties 文件,以及为示例应用程序提供服务定义的 library.wsdl 文件,用于配置客户端登录的 log4j.properties 文件,以及一些属性定义 XML 文件(全部命名为 XXX-policy-client.xml 或 XXX-policy-server.xml)。build.properties 文件配置示例应用程序的操作。清单 2 展示了这个属性文件的附带版本:

清单 2. 附带的 build.properties 文件
# set axis-home to your Axis2 installation directory
axis-home=PATH_TO_AXIS2_INSTALLATION
# set the connection protocol to be used to access services (http or https)
protocol=http
# set the name of the service host
host-name=localhost
# set the port for accessing the services (change this for monitoring)
host-port=8080
# set the base path for accessing all services on the host
base-path=/axis2/services/
# set the name of the policy file to be used by the client
client-policy=plain-policy-client.xml
# set the name of the policy file to be used by the server
server-policy=plain-policy-server.xml

在尝试使用示例应用程序前,需要编辑 build.properties 文件并将实际路径设置为 Axis2 安装(添加了 Rampart,如前一小节所述)。如果对服务器使用了不同的主机或端口号,还需要修改 host-namehost-port 值。我将在本文后面讨论其余的值。

尝试使用 WS-Security

WS-Security 定义了一些不同的安全令牌类型(包括构成核心规范的令牌以及在配置文件 中定义为规范的插件扩展的令牌),并定义了多种方法来构建和使用令牌。本文的目的就是使用 Axis2 配置和 Rampart,因此我将使用最简单的令牌作为示例:由 UsernameToken 配置文件定义的 UsernameToken

UsernameToken WS-SecurityPolicy

UsernameToken 的功能仅仅是将用户名和密码信息作为 WS-Security 头部的一部分进行传递。UsernameToken 的最基本形式就是以明文的方式发送用户名和密码。从安全角度来看这并不合适(但是在安全连接之上使用这种方法并不会出问题),但是很容易看到发送的内容,因此是一个非常好的起点。

UsernameToken 以文本形式发送的 WS-SecurityPolicy 配置非常简单,如 清单 3 所示。该策略(在这里将一行代码显示为两行以匹配页面宽度 — 实际使用时这样做是无效的)包含了一个标准 WS-Policy 包装器(使用 wsp 前缀的元素),用来封装一个 WS-SecurityPolicy UsernameToken 断言。

清单 3. 用于明文形式的 UsernameToken 的 WS-SecurityPolicy
<wsp:Policy wsu:Id="UsernameToken" xmlns:wsu=
    "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
    xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
  <wsp:ExactlyOne>
    <wsp:All>
      <sp:SupportingTokens
          xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
        <wsp:Policy>
          <sp:UsernameToken sp:IncludeToken="http://docs.oasis-open.org/
               ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient"/>
        </wsp:Policy>
      </sp:SupportingTokens>
    </wsp:All>
  </wsp:ExactlyOne>
</wsp:Policy>

清单 3 中的 UsernameToken 使用一个 IncludeToken 属性指定将包含到令牌中的消息流的类型 — 在本例中,所有消息流从请求发起者(即客户机)流向请求接收者(即服务器)。还可以为 IncludeToken 属性定义其他值来指定不同的令牌用途,但是对于 UsernameToken,这通常是惟一有效的值。

应用策略

WS-Policy 和 WS-SecurityPolicy 旨在支持 WSDL 服务定义中的内嵌功能。使用引用将一个策略关联到一个或多个 <wsdl:binding><wsdl:binding>/<wsdl:operation><wsdl:message> 定义。Axis2 1.4.X 为 WSDL 中内嵌的策略实现了初步处理,但是直到 Axis2 1.4.1,实现仍然不够健壮。本文将把策略直接连接到客户机和服务器,从而实现与 1.4.1 代码的兼容性。

服务器端策略处理

在服务器端,应用策略的方式是将其添加到每个 Axis2 .aar 服务归档中的 services.xml 配置文件。策略可以作为 <service> 元素的子元素直接添加,以应用于服务定义的所有操作。还需要向 services.xml 添加一个 <module> 元素,这是为了告诉 Axis2 Rampart 模块必须包含在服务的配置中。清单 4 是示例应用程序使用的经过编辑的 services.xml,其中粗体显示的是添加的模块引用和策略信息:

清单 4. 包含内嵌策略的 services.xml
<serviceGroup>
  <service name="library-username">
    <messageReceivers>
      <messageReceiver
          class="com.sosnoski.ws.library.adb.LibraryUsernameMessageReceiverInOut"
          mep="http://www.w3.org/ns/wsdl/in-out"/>
    </messageReceivers>
    <parameter
        name="ServiceClass">com.sosnoski.ws.library.adb.LibraryUsernameImpl</parameter>
    <parameter name="useOriginalwsdl">true</parameter>
    <parameter name="modifyUserWSDLPortAddress">true</parameter>
    <operation mep="http://www.w3.org/ns/wsdl/in-out" name="getBook"
        namespace="http://ws.sosnoski.com/library/wsdl">
      <actionMapping>urn:getBook</actionMapping>
      <outputActionMapping>http://.../getBookResponse</outputActionMapping>
    </operation>
    ...

    <module ref="rampart"/>
    <wsp:Policy xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
        xmlns:wsu="http://.../oasis-200401-wss-wssecurity-utility-1.0.xsd"
        wsu:Id="UsernameToken">
      <wsp:ExactlyOne>
        <wsp:All>
          <sp:SupportingTokens
              xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
            <wsp:Policy>
              <sp:UsernameToken
                  sp:IncludeToken="http://.../IncludeToken/AlwaysToRecipient">
                <wsp:Policy>
                  <sp:HashPassword/>
                </wsp:Policy>
              </sp:UsernameToken>
            </wsp:Policy>
          </sp:SupportingTokens>

          <ramp:RampartConfig xmlns:ramp="http://ws.apache.org/rampart/policy">
            <ramp:passwordCallbackClass>...PWCBHandler</ramp:passwordCallbackClass>
          </ramp:RampartConfig>

        </wsp:All>
      </wsp:ExactlyOne>
    </wsp:Policy>
  </service>
</serviceGroup>

如果对 清单 4 中的内嵌策略与 清单 3 中的基本策略进行比较,会发现多了一项内容 — 一个 <ramp:RampartConfig> 元素。该元素为策略信息提供了特定于 Rampart 的扩展,在本例中,给出了将用于处理密码回调的类的名称。服务器代码使用回调检验客户机在请求中提供的用户名和密码组合。

清单 5 展示了回调类的实际实现,用于明文形式的密码。在本例中,用户名和密码都被提供给回调,并且所有回调只需要检验用户名和密码组合。如果用户名和密码匹配预期值,那么返回即可;否则,将抛出一个异常表示出错。

清单 5. 密码回调代码
import org.apache.ws.security.WSPasswordCallback;

public class PWCBHandler implements CallbackHandler
{
  public void handle(Callback[] callbacks)
    throws IOException, UnsupportedCallbackException {
      for (int i = 0; i < callbacks.length; i++) {
        WSPasswordCallback pwcb = (WSPasswordCallback)callbacks[i];
          String id = pwcb.getIdentifer();
            if (pwcb.getUsage() == WSPasswordCallback.USERNAME_TOKEN_UNKNOWN) {

              // used when plain-text password in message
                if (!"libuser".equals(id) || !"books".equals(pwcb.getPassword())) {
                  throw new UnsupportedCallbackException(callbacks[i], "check failed");
                }
            }
        }
    }
}

对于一个真正的应用程序,您肯定希望使用其他一些机制(比如数据库或外部安全机制)来检验用户名和密码组合。回调技术可以让您使用任何检查技巧来扩展 Rampart 安全处理。

客户端配置

要对客户机代码使用 Rampart,首先需要对 Axis2 使用模块。方法就是针对客户机配置一个 Axis2 库结构,但是更简单的方法是在您的类路径中包含 rampart.mar 模块文件(以及需要使用的其他模块)。提供的示例使用了类路径方法。

然后需要为客户机配置安全模块和其他相关参数。处理此配置的最简单方法就是直接在服务 stub 上设置值。清单 6 展示了示例代码中的配置过程:

清单 6. 客户机配置
    /**
     * Load policy file from classpath.
     */
    private static Policy loadPolicy(String name) throws XMLStreamException {
        ClassLoader loader = WebServiceClient.class.getClassLoader();
        InputStream resource = loader.getResourceAsStream(name);
        StAXOMBuilder builder = new StAXOMBuilder(resource);
        return PolicyEngine.getPolicy(builder.getDocumentElement());
    }

    public static void main(String[] args) throws IOException, XMLStreamException {

        // check for required command line parameters
        if (args.length < 4) {
            System.out.println("Usage:\n  java " +
                "com.sosnoski.ws.library.adb.WebServiceClient protocol host port path");
            System.exit(1);
        }

        // create the client stub
        String target = args[0] + "://" + args[1] + ":" + args[2] + args[3];
        System.out.println("Connecting to " + target);
        LibraryUsernameStub stub = new LibraryUsernameStub(target);

        // configure and engage Rampart
        ServiceClient client = stub._getServiceClient();
        Options options = client.getOptions();
        options.setProperty(RampartMessageData.KEY_RAMPART_POLICY,
            loadPolicy("policy.xml"));
        options.setUserName("libuser");
        options.setPassword("books");
        client.engageModule("rampart");

配置内容包含在清单 6 中的最后一个代码块。包括从创建的 stub 中获得 org.apache.axis2.client.ServiceClient 实例并在客户机选项中设置策略信息(从类路径加载)和用户名/密码。随后在客户机使用的 Axis2 配置中加入 Rampart 模块。完成这些操作后,可以使用 stub 访问服务,就像不存在 WS-Security 一样,而 Rampart 将 UsernameToken 自动添加到每个请求。

确认结果

安装 Ant 后,可以从打开了示例代码目录的控制台运行 ant,以构建客户机和服务器代码。随后可以将创建的 library-username.aar 文件部署到 Axis2 服务器安装(当然,包含 Rampart .jars 和 .mars),并通过在控制台输入 ant run 来运行客户机。如果一切顺利的话,应当看到如 图 1 所示的输出:

图 1. 运行应用程序时的控制台输出
运行应用程序时的控制台输出
运行应用程序时的控制台输出

当然,仅仅用服务器运行客户机并不能向您表明发生的操作。可以使用 TCPMon 等工具充当客户机和服务器之间的中间层,从而捕获消息交换以查看 WS-Security UsernameToken 的行为(见 参考资料)。为此,首先需要设置 TCPMon 并在一个端口上接受来自客户机的连接,这个端口随后将连接转发给运行在不同端口(或不同主机)上的服务器。然后可以编辑 build.properties 文件并将 host-port 的值修改为侦听 TCPMon 的端口。如果再一次在控制台中输入 ant run,应当会看到消息发生了交换。清单 7 展示了一个样例客户机消息捕捉:

清单 7. 使用 UsernameToken 的客户机消息
<?xml version='1.0' encoding='UTF-8'?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Header>
    <wsse:Security xmlns:wsse="...wss-wssecurity-secext-1.0.xsd"
        soapenv:mustUnderstand="1">
      <wsse:UsernameToken xmlns:wsu="...wss-wssecurity-utility-1.0.xsd"
          wsu:Id="UsernameToken-1815911473">
        <wsse:Username>libuser</wsse:Username>
        <wsse:Password Type="...wss-username-token-profile-1.0#PasswordText"
            >books</wsse:Password>
      </wsse:UsernameToken>
    </wsse:Security>
  </soapenv:Header>
  <soapenv:Body>
    <ns2:getBooksByType xmlns:ns2="http://ws.sosnoski.com/library/wsdl">
      <ns2:type>scifi</ns2:type>
    </ns2:getBooksByType>
  </soapenv:Body>
</soapenv:Envelope>

保护 UsernameToken

一个基本的明文形式的 UsernameToken 并不能直接提供很强的安全性,因为用户名和相应的密码对于任何能够监视消息的人来说都是可见的。如果使用加密的通信通道,那么就可以解决这个问题 — 只要通道加密非常可靠,外部无法监视消息。WS-SecurityPolicy 方便地定义了一种方法来使用加密过的通道,如 清单 8 所示(同样,将一行代码分解为两行以匹配页面宽度 — 查看示例代码包的 secure-policy-server.xml 文件获得真实的策略):

清单 8. 要求 HTTPS 连接的策略
<wsp:Policy wsu:Id="UsernameToken" xmlns:wsu=
  "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
  xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
 <wsp:ExactlyOne>
  <wsp:All>
   <sp:TransportBinding xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
     <wsp:Policy>
      <sp:TransportToken>
        <wsp:Policy>
         <sp:HttpsToken RequireClientCertificate="false"/>
        </wsp:Policy>
      </sp:TransportToken>
     </wsp:Policy>
   </sp:TransportBinding>
   <sp:SupportingTokens
     xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
    <wsp:Policy>
     <sp:UsernameToken sp:IncludeToken="http://docs.oasis-open.org/
          ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient"/>
    </wsp:Policy>
   </sp:SupportingTokens>
  </wsp:All>
 </wsp:ExactlyOne>
</wsp:Policy>

清单 8 中使用粗体显示的代码是新增的部分,由 <sp:TransportBinding> 元素和嵌套的 <sp:HttpsToken> 元素组成。<sp:HttpsToken> 元素表示一个安全的 HTTPS 连接必须用于与服务的通信。如果尝试使用此策略构建 service .aar(通过将 build.properties 中的 server-policy 值修改为 secure-policy-server.xml,然后运行 ant build-server)并部署它,将看到 Rampart 实施这项策略需求,拒绝任何普通的 HTTP 连接。

如果希望对服务使用一个 HTTPS 连接,这样做是可行的,但是首先需要配置您的 Web 服务器以支持 HTTPS。(Tomcat 提供了非常好的说明,可以从 /tomcat-docs/ssl-howto.html 访问)。还需要在 build.properties 中将 protocol 值修改为 https,并且如果对 Web 服务器使用的是自签名的证书,在运行 Ant test 目标时需要向客户机发送一个证书存储库(trust store)。提供的 build.xml 有一个注释掉的行可以实现这点,因此只需去掉此行的注释符号,并设置相应的证书存储库文件在系统中的位置。

另一种增强 UsernameToken 安全性的方法甚至可以处理未加密的链接。这种方法使用对一个字符串计算得到的摘要(digest)值,该字符串由两个文本值和密码组成。其中一个文本值为 nonce,是由发送者对每个请求生成的随机值。另一个文本值是一个生成的时间戳,表示发送者创建 UsernameToken 的时间。这两个值都以明文形式包含在 UsernameToken 中。如果恰当地应用于客户机和服务器,组合这些值和摘要中的密码使服务器能够验证在生成摘要时使用的是正确的密码,同时使得外部很难伪造有效的密码。清单 9 给出了一个使用摘要密码的策略示例,其后是实际捕获的使用摘要密码的消息(全部重新格式化以适合页面宽度 — 查找 hash-policy-client.xml 文件获得真实的策略),粗体显示的是与初始策略不同的地方。

清单 9. 使用密码摘要的策略和样例消息
<wsp:Policy wsu:Id="UsernameToken" xmlns:wsu=
    "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
    xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
  <wsp:ExactlyOne>
    <wsp:All>
      <sp:SupportingTokens
          xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
        <wsp:Policy>
          <sp:UsernameToken sp:IncludeToken="http://docs.oasis-open.org/
ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
            <wsp:Policy>
              <sp:HashPassword/>
            </wsp:Policy>
          </sp:UsernameToken>
        </wsp:Policy>
      </sp:SupportingTokens>
    </wsp:All>
  </wsp:ExactlyOne>
</wsp:Policy>

<?xml version='1.0' encoding='UTF-8'?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Header>
    <wsse:Security xmlns:wsse=".../oasis-200401-wss-wssecurity-secext-1.0.xsd"
        soapenv:mustUnderstand="1">
      <wsse:UsernameToken xmlns:wsu="...wss-wssecurity-utility-1.0.xsd"
          wsu:Id="UsernameToken-1421876889">
        <wsse:Username>libuser</wsse:Username>
        <wsse:Password Type="...wss-username-token-profile-1.0#PasswordDigest"
          >/Wt/2yDdZwa8a5qd7U70hrp29/w=</wsse:Password>
        <wsse:Nonce>4ZQz5ytME/RXfChuKJ03iA==</wsse:Nonce>
        <wsu:Created>2009-03-17T11:20:57.467Z</wsu:Created>
      </wsse:UsernameToken>
    </wsse:Security>
  </soapenv:Header>
  <soapenv:Body>
    <ns2:getBooksByType xmlns:ns2="http://ws.sosnoski.com/library/wsdl">
      <ns2:type>scifi</ns2:type>
    </ns2:getBooksByType>
  </soapenv:Body>
</soapenv:Envelope>

结束语

在本文中,您了解了如何使用 Axis2 和 Rampart 实现基本的基于策略的 WS-Security 处理。在下一期 Java Web 服务 中,将了解 WS-Security 的两个强大特性:XML 加密和签名。使用 XML 加密可以使您在操作任何类型的连接时保持消息内容的私密性,即使在处理过程中涉及到不可信的代理。使用 XML 签名可以确保消息确实来自声称的发起者,并且消息内容在传输过程中没有被篡改。加密和签名是大多数企业安全实现的基础,回顾一下这些特性,看看如何在您自己的 Web 服务中应用它们。


下载资源


相关主题

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Java technology, SOA and web services
ArticleID=404974
ArticleTitle=Java Web 服务: Axis2 WS-Security 基础
publish-date=06302009