Java Web服务:WS-SecurityPolicy建模与验证

日期: 2011-08-14 作者:Dennis Sosnoski 来源:TechTarget中国 英文

  WS-SecurityPolicy的一个缺点是在编写一个策略时很容易出现错误,如断言位置使用不当,或者在文档中混合使用断言版本等。许多web servicess协议会悄悄忽略这些错误,这意味着您的策略不会按预期方式发挥作用,并且您需要花时间去分析策略才能发现问题根源。

  在本文的第一部分,您将了解到为什么WS-SecurityPolicy很难使用,并且会了解一些常见的WS-SecurityPolicy错误的发生原因。第二部分将介绍一个WS-Policy和WS-SecurityPolicy数据模型,它支持文档结构的验证,然后介绍如何使用JiBX数据绑定来解析模型中的文档。

  用WSDL描述WS-Policy

  “Java web服务:理解WS-Policy”详细介绍了WS-Policy的大部分内容,包括如何将策略断言附加到WSDL 1.1组件上。简而言之,WS-Policy允许您将策略附加到 WSDL 服务定义中对应消息和消息组的不同位置上。WS-Policy使用了以下4个级别的消息分组:

  消息(Message)—策略会应用到一条特殊的消息(这个消息会在任何通过<wsdl:message>元素附加策略的地方使用,或者由通过某个操作定义的input/output/fault附加策略的特殊操作使用,这个操作定义位于<wsdl:portType>或<wsdl:binding>元素中)。

  操作(Operation)—策略会应用到某个特殊操作中交换的全部消息(策略是通过<wsdl:operation>元素附加的,位于<wsdl:binding>或<wsdl:portType>元素中)。

  端点(Endpoint)—策略会应用到某个特定服务绑定中交换的全部消息(策略是通过<wsdl:port>或<wsdl:binding>附加的),或者基于特殊的端口类型应用到全部服务绑定中(策略会附加到对应<wsdl:portType>上)。

  服务—策略会应用到所有端点或与服务相关联的所有操作上(策略是在<wsdl:service>元素中附加)。

  应用到某个级别的消息分组的策略是由底层继承的,这样应用到每一条消息的实际(用WS-Policy词汇表示就是有效)策略就是应用到消息、操作、终端和服务层的所有策略的联合。所以策略不仅仅决定于消息本身,也受消息所使用的上下文影响。

  WS-Policy附件技术

  在 “Java web服务:理解WS-Policy” 一文中,我介绍了将WS-Policy附加到WSDL组件的最常用方法:使用<wsp:PolicyReference>元素。大多数情况下,这种通过引用实现的附加方法都适用,但是这不是惟一的方法。除此之外,您还可以在与一个消息分组级别对应的任意WSDL元素上使用一个特殊属性—wsp:PolicyURIs—来实现。这个属性的值是一个应用到该元素的策略-断言清单。您还可以通过直接嵌入策略断言而将策略断言附加到一个WSDL元素上。

  各种附加策略断言方法的选择大多数情况下是任意的。wsp:PolicyURIs 属性可以用在一些不支持根据原始 WSDL 模式来扩展元素的 WSDL 元素上 — 但是最新的 WS-I WSDL 模式允许在所有 WSDL 元素上增加元素,这样大多数 web services 工具都是支持的。使用 <wsp:PolicyReference> 元素或 wsp:PolicyURIs 属性使您能够重用策略断言,包括外部策略断言(但是并非所有 web services 工具都支持外部引用)。

  另一种以外部方式附加策略的方法是使用<wsp:PolicyAttachment>元素。理论上,这种方法允许您将策略关联到WSDL之外的服务。然而,大多数web services工具并不支持这种附加方式,而是在WSDL服务描述语言中引用或直接包含策略信息。

  <wsp:PolicyReference>使能够定义提取算法,使用可选属性来提取所引用策略的值。这个提取算法为外部策略引用提供了一些安全性保证,但是它不能保护引用本身的修改。<wsp:PolicyAttachment>则更进一步,它支持直接使用WS-Security来保证整个附件的安全性。

  WS-SecurityPolicy附件限制

  WS-SecurityPolicy指定了消息分组级别,其中特定类型的策略断言会被附加到一个服务描述中:

  安全绑定断言:<sp:TransportBinding> 只支持终端级别(因为这里使用 transport 来访问终端);<sp:SymmetricBinding> 和 <sp:AsymmetricBinding> 都支持终端或操作级别

  支持令牌断言(所有形式的支持令牌,包括 <sp:SupportingTokens>和<sp:SignedSupportingTokens>等等):终端、操作或消息级别
  选项断言(<sp:Wss10>、<sp:Wss11>和<sp:Trust13>):支持终端级别

  保护断言(<sp:SignedParts>、<sp:SignedElements>、<sp:EncryptedParts>、<sp:EncryptedElements>、<sp:ContentEncryptedElements>、<sp:RequiredElements>和<sp:RequiredParts>):支持消息级别

  这些级别只是建议,所以web servicess工具应该是能够处理各种不同的附加级别的—但是最好在可能的情况下遵循这些建议。

  上面未列出的WS-SecurityPolicy断言是嵌在其他断言中的,所以所包含的断言位置决定了它们的级别。

  使用WS-SecurityPolicy

  WS-SecurityPolicy使用了一种很难直接编写或修改的复杂且容易出错的结构。大多数商业web servicess工具都提供了一些GUI工具,您可以用它的一些菜单选项生成WS-SecurityPolicy文档。(开源Metro项目也提供了这种工具,它是NetBeans的一部分。)这类工具的输出结果与提供这个工具的web services栈能够很好地配合,但是它可能并非最佳实践方法,甚至可能不完全正确。如果您不使用这样的工具,而希望修改一个工具不支持的安全性配置,或者只是希望保证您的策略是正确的,那么您必须自己处理复杂的WS-SecurityPolicy。

  我本身并不太喜欢GUI WS-SecurityPolicy配置工具,所以在这个系列文章中,我都是直接处理策略文档的。在测试不同类型的安全性配置时,我遇到了许多的错误,它们总是在WS-SecurityPolicy复杂性上面临挑战,也使我认识到有很多方法可能会导致错误。我将首先介绍为什么我认为WS-SecurityPolicy是很复杂的,然后介绍我在开始正确的可互操作的WS-SecurityPolicy配置时经常遇到的问题。

  WS-SecurityPolicy结构

  WS-SecurityPolicy 1.2模式定义了约140种元素,几乎所有元素都是全局定义的(这意味着它们理论上都可以单独使用,而不需要嵌入到其他元素或文档中)。它们中的大多数都被定义为空的标记元素,包含了其他名称空间支持的扩展属性,但不包含内容。而其他元素则同时支持扩展属性和其他名称空间的元素,但是不支持WS-SecurityPolicy名称空间的元素。WS-SecurityPolicy 1.3模式是1.2版本的扩展,增加了一些新的元素。

  不管是1.2版本模式还是1.3版本的扩展模式在实践中都不太实用。这并非是模式作者的错误,而是由XML模式本身的限制和WS-Policy的结构造成的。

  正如在 “Java web服务:理解WS-Policy” 所介绍的,WS-Policy提供了一个简单的定义和组合策略断言的框架,而断言本身则由诸如 WS-SecurityPolicy 的其他扩展定义,其中每一个扩展都有其自己的名称空间。清单 1是一个WS-SecurityPolicy示例文档,其中粗体显示的是 WS-Policy元素:

  清单 1. WS-SecurityPolicy示例

以下是引用片段:
        <wsp:Policy wsu:Id=”SecureConv”
   
   
   >
  <wsap:UsingAddressing />
  <sp:SymmetricBinding>
   <wsp:Policy>
    <sp:ProtectionToken>
     <wsp:Policy>
      <sp:SecureConversationToken sp:IncludeToken=
        “http://…/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient”>
       <wsp:Policy>
        <sp:RequireDerivedKeys/>
        <sp:BootstrapPolicy>
         <wsp:Policy>
          <sp:AsymmetricBinding>
           <wsp:Policy>
            <sp:InitiatorToken>
             <wsp:Policy>
              <sp:X509Token sp:IncludeToken=
                “http://…/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient”>
               <wsp:Policy>
                <sp:RequireThumbprintReference/>
               </wsp:Policy>
              </sp:X509Token>
             </wsp:Policy>
            </sp:InitiatorToken>
            <sp:RecipientToken>
             <wsp:Policy>
              <sp:X509Token sp:IncludeToken=
                “http://…/ws-securitypolicy/200702/IncludeToken/AlwaysToInitiator”>
               <wsp:Policy>
                <sp:RequireThumbprintReference/>
               </wsp:Policy>
              </sp:X509Token>
             </wsp:Policy>
            </sp:RecipientToken>
            <sp:AlgorithmSuite>
             <wsp:Policy>
              <sp:TripleDesRsa15/>
             </wsp:Policy>
            </sp:AlgorithmSuite>
            <sp:Layout>
             <wsp:Policy>
              <sp:Strict/>
             </wsp:Policy>
            </sp:Layout>
            <sp:IncludeTimestamp/>
            <sp:OnlySignEntireHeadersAndBody/>
           </wsp:Policy>
          </sp:AsymmetricBinding>
          <sp:SignedParts>
           <sp:Body/>
           <sp:Header Namespace=”http://www.w3.org/2005/08/addressing”/>
          </sp:SignedParts>
          <sp:EncryptedParts>
           <sp:Body/>
          </sp:EncryptedParts>
          <sp:Trust13>
           <wsp:Policy>
            <sp:MustSupportIssuedTokens/>
            <sp:RequireClientEntropy/>
            <sp:RequireServerEntropy/>
           </wsp:Policy>
          </sp:Trust13>
         </wsp:Policy>
        </sp:BootstrapPolicy>
       </wsp:Policy>
      </sp:SecureConversationToken>
     </wsp:Policy>
    </sp:ProtectionToken>
    <sp:AlgorithmSuite>
     <wsp:Policy>
      <sp:Basic128Rsa15/>
     </wsp:Policy>
    </sp:AlgorithmSuite>
    <sp:Layout>
     <wsp:Policy>
      <sp:Strict/>
     </wsp:Policy>
    </sp:Layout>
   </wsp:Policy>
  </sp:SymmetricBinding>
  <sp:EncryptedParts>
   <sp:Body/>
  </sp:EncryptedParts>
</wsp:Policy>

  清单 1的策略文档主要由WS-Policy元素轮换层(在这里只有<wsp:Policy>元素)和WS-SecurityPolicy元素(使用sp前缀)构成。可惜,XML模式不能表示这种结构。每一个模式定义都对应一个名称空间,虽然它可以引用和使用其他名称空间中定义的元素,但是它不能为这些元素定义子结构。所以WS-Policy的模式定义可以定义一个普通结构,而WS-SecurityPolicy模式可以定义一些准备与该结构组合使用的元素,但是现在没有办法定义这些两元素之间的交互。

  由XML模式无法定义文档结构,所以WS-SecurityPolicy标准内容中使用了与WS-Policy推荐相同的语法表现方式来作为文档的预期结构。清单 2显示了标准提供的一个示例文档:

  清单 2. WS-SecurityPolicy语法示例
    

以下是引用片段:
<sp:SymmetricBinding  … >
   <wsp:Policy >
     (
       <sp:EncryptionToken … >
         <wsp:Policy> … </wsp:Policy>
       </sp:EncryptionToken>
       <sp:SignatureToken … >
         <wsp:Policy> … </wsp:Policy>
       </sp:SignatureToken>
     ) | (
       <sp:ProtectionToken … >
         <wsp:Policy> … </wsp:Policy>
       </sp:ProtectionToken>
     )
     <sp:AlgorithmSuite … > … </sp:AlgorithmSuite>
     <sp:Layout … > … </sp:Layout> ?
     <sp:IncludeTimestamp … /> ?
     <sp:EncryptBeforeSigning … /> ?
     <sp:EncryptSignature … /> ?
     <sp:ProtectTokens … /> ?
     <sp:OnlySignEntireHeadersAndBody … /> ?
     …
   </wsp:Policy>
   …
</sp:SymmetricBinding>

  清单 2 中使用的语法对于大多数开发人员而言是非常容易理解的:使用圆括号来划分组,使用 | 字符表示备选项,以及使用?表示可选组件。(完整的语法还使用 * 表示0次或多次出现,使用 + 表示1次或多次出现。)但是,这并非标准的XML语法,所以我们无法直接用它来验证实例文档。

  结构错误

  鉴于WS-SecurityPolicy的复杂性和检查文档是否符合标准的难度,很自然处理文档时遇到的最常见问题就是将断言添加到错误的位置。例如,如果您将<EncryptedParts>元素(清单 1末尾)移动到上述<SymmetricBinding>元素的Policy内部时,您就得到一个不符合WS-SecurityPolicy标准的结构。对于这种结构错误的策略进行解析的结果是不确定的,并且是由具体的web services实现决定的—它可能会正常解析,但是很可能无法兼容其他的实现技术。

  web services 实现技术可能会或不会向用户报告结构错误。在本系列文章中介绍的三种开源web services实现技术(Apache Axis2 1.5.4、 Metro 2.1和Apache CXF 2.3.3)的最新版本中测试上述EncryptedParts元素位置错误的例子时,只有Metro报告了错误和拒绝操作。Axis和CXF都接受了这个策略而执行任何错误,但是运行后不会加密消息体。这种不报告错误的问题使我们很难发现由于策略结构错误导致的问题。

  版本冲突

  名称空间是最容易出现WS-Policy和WS-SecurityPolicy问题的地方。WS-Policy和WS-SecurityPolicy的标准化已经很多年了,但是这些技术的早期版本在标准发布之前已经被广泛应用了。虽然大多数官方标准都兼容早期版本的XML结构,但是会使用不同的名称空间,所以某个特定文档应该应用哪些规则集是很明显的。

  大多数工具都支持官方推荐和早期版本的WS-Policy和WS-SecurityPolicy,从而使这些版本能够互相兼容。(惟一例外的是Axis2,它只支持WS-Policy的最新发布的1.5.4的提交版本。)尽管具有这样的灵活性,但是在一个文档中使用不同的名称空间还是可能会出现问题。在实践中,我们是不需要混合使用WS-Policy或WS-SecurityPolicy名称空间的,但是当您为了创建一个新的策略而组合几个策略的某些部分时,就很容易出现这种意外错误。所产生的冲突可能包括元素所使用的名称空间和使用令牌的值中的实际内容值。例如,WS-SecurityPolicy 1.2和1.3都使用http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/Always来表示消息中总是应该包含一个令牌。而旧1.1版本则使用http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/Always来实现同样的功能。

  因为在标准的各个版本中这些XML元素的含义一般不会发生变化,个别工具可能会在处理策略文档时选择忽略名称空间差别。采用这个方法的工具很可能会接受这些混合名称空间的策略而不报告任何错误。但是,通常最好要遵守官方推荐的名称空间,特别是要避免混合使用旧的名称空间和推荐的名称空间。

  WS-Policy和WS-SecurityPolicy建模

  在Java语言中进行WS-Policy和WS-SecurityPolicy建模相对于在“Java Web服务:WSDL 1.1理解与建模” 中讨论的WSDL 1.1建模而言是完全不同的挑战。然而,WSDL建模的主要问题是它允许许多常用元素的排序变化,WS-Policy和WS-SecurityPolicy会完全忽略元素的顺序,而提供一种在上层应用了许多规则的宽松结构。这意味着一个<wsp:Policy>元素(或任何其他的WS-Policy操作符变化)的预期内容是完全取决于元素的上下文。清单 1演示了这一点,它文档里面几乎全部15个<wsp:Policy>元素都具有不同的内容模型。

  数据模型

  WS-SecurityPolicy断言主要分成两类:标记器断言(空元素),它们的出现只是为了表示某些特性或功能;或者结构化断言,它嵌入了一个包含其他断言的策略。当然,结构化断言正是造成复杂性的原因所在。

  清单 3 所示代码是处理基于模型的NestedPolicy基类和相关的VerificationHandler接口实现的结构化断言:

  清单 3. NestedPolicy和VerificationHandler
    

以下是引用片段:
public abstract class NestedPolicy extends AssertionBase {
   /** Nested policy definition. */
   private Policy m_policy;
   
   /** Arbitrary extension elements. */
   private List<Element> m_extensions = new ArrayList<Element>();
   …
   /**
    * Create a handler instance for a set of assertions.
    * 
    * @return instance
    */
   public abstract VerificationHandler createHandler();
   
   /**
    * Verify policy goodness. This uses a handler supplied by the {@link
    * #createHandler()} method to verify all the assertions within the nested policy in
    * the context of this assertion.
    * 
    * @param vctx
    */
   public void verify(ValidationContext vctx) {
       for (Set<AssertionBase> asserts : m_policy.getAlternatives()) {
           VerificationHandler handler = createHandler();
           for (AssertionBase asser : asserts) {
               if (asser instanceof ExtensibleMarker) {
                   handler.addMarker((ExtensibleMarker)asser, vctx);
               } else {
                   handler.addGeneral(asser, vctx);
               }
           }
           handler.complete(vctx);
       }
   }
}
public interface VerificationHandler
{
    /**
     * Add marker assertion.
     * 
     * @param marker
     * @param vctx
     */
    public abstract void addMarker(ExtensibleMarker marker, ValidationContext vctx);
    
    /**
     * Add general assertion (anything that’s not a marker).
     * 
     * @param asser
     * @param vctx
     */
    public abstract void addGeneral(AssertionBase asser, ValidationContext vctx);
    
    /**
     * Check that the assertions included in this collection fulfill all requirements for
     * the policy. This method is only used when verifying a complete policy (one
     * particular combination of alternatives, when using alternatives).
     * 
     * @param vctx
     * @return <code>true</code> if no errors, <code>false</code>
     * if error
     */
    boolean complete(ValidationContext vctx);
}

  所有结构化断言都扩展了NestedPolicy类,实现了createHandler()方法,它返回一个根据具体断言结构调整的VerificationHandler接口实例。VerificationHandler提供了用于累加和验证嵌套策略中包含的断言的方法,这就是结构化断言处理的实现细节所在。当出现策略替代时,NestedPolicy.verify()方法会为每一个替代选择创建一个独立的VerificationHandler实例。

  清单4和清单5一起演示了一个具体的结构化断言处理例子,以类的形式来表示和验证一个SymmetricBinding断言。清单 4所示的SymmetricBinding类是非常简单的,它只是基于 清单 5 所示的BindingAssertionHandler类定义了一个内联类来处理验证过程:

  清单 4. SymmetricBinding
    

以下是引用片段:
public class SymmetricBinding extends NestedPolicy {
    public VerificationHandler createHandler() {
        return new AssertionHandler();
    }
    
    private static class AssertionHandler extends BindingAssertionHandler
    {
        /** Containing element name. */
        private static final String ELEMENT_NAME = “SymmetricBinding”;
        
        /** Names of allowed token role elements. */
        private static final Set<String> TOKEN_ROLES =
            VerificationHandlerBase.buildNameSet(“EncryptionToken|…|ProtectionToken”);
        
        protected AssertionHandler() {
            super(ELEMENT_NAME, TOKEN_ROLES,
                BindingAssertionHandler.ENCRYPTION_BINDING_MARKERS);
        }
        public boolean complete(ValidationContext vctx) {
            boolean good = true;
            Map<String, TokenProperty> tokens = getRoleTokens();
            if (tokens.containsKey(“ProtectionToken”)) {
                if (tokens.containsKey(“EncryptionToken”)) {
                    vctx.reportError(“sp:ProtectionToken conflicts …”, this);
                    good = false;
                }
                if (tokens.containsKey(“SignatureToken”)) {
                    vctx.reportError(“sp:ProtectionToken conflicts …”, this);
                    good = false;
                }
            } else if (!tokens.containsKey(“EncryptionToken”) && 
              !tokens.containsKey(“SignatureToken”)) {
                vctx.reportWarning(“No tokens defined for binding”, this);
            }
            return super.complete(vctx) && good;
        }
    }
}

  SymmetricBinding.AssertionHandler内联类定义了一组为<sp:SymmetricBinding>定义的令牌角色,也实现了VerificationHandler.complete()方法来检查至少有一种令牌出现,并且这些令牌不存在冲突。(<sp:SymmetricBinding>支持可用于消息签名和加密的<sp:ProtectionToken>,或者单独的<sp:EncryptionToken>和/或<sp:SignatureToken>。)

  如清单 5 所示,BindingAssertionHandler是一个支持全部三种绑定断言(transport、asymmetric和symmetric)的通用基类。这里为每一种断言都定义了一个或多个标记器断言,一个或多个令牌角色,一个必需的<sp:AlgorithmSuite> 和一个可选的<sp:Layout>(最后两个断言带有子断言,但是没有嵌套策略)。

  清单 5. BindingAssertionHandler
    

以下是引用片段:
public class BindingAssertionHandler extends VerificationHandlerBase {
    /** Names of marker elements allowed in &lt;TransportBinding>. */
    public static final Set<String> TRANSPORT_BINDING_MARKERS =
        VerificationHandlerUtils.buildNameSet(“IncludeTimestamp”);
    
    /** Names of marker elements allowed in … or &lt;SymmetricBinding>. */
    public static final Set<String> ENCRYPTION_BINDING_MARKERS =
        VerificationHandlerUtils.
        buildNameSet(“IncludeTimestamp|…|OnlySignEntireHeadersAndBody”);
    
    /** Actual element name. */
    private final String m_elementName;
    
    /** Roles allowed for tokens. */
    private final Set<String> m_tokenRoles;
    
    /** Token properties for binding. */
    private final Map<String,TokenProperty> m_roleTokens;
    
    /** Marker assertions allowed in policy. */
    private final Set<String> m_knownMarkers;
    
    /** Marker token assertions. */
    private final Map<String,ExtensibleMarker> m_nameMarkers;
    …
    protected BindingAssertionHandler(String name, Set<String> roles,
        Set<String> markers) {
        m_elementName = name;
        m_tokenRoles = roles;
        m_roleTokens = new HashMap<String,TokenProperty>();
        m_knownMarkers = markers;
        m_nameMarkers = new HashMap<String,ExtensibleMarker>();
    }
    …
    public void addMarker(ExtensibleMarker marker, ValidationContext vctx) {
        String name = marker.getName();
        if (m_knownMarkers.contains(name)) {
            // generate warning for duplicate assertion
            VerificationHandlerUtils.checkRepeat(marker, m_nameMarkers, vctx);
        } else {
            vctx.reportError(“Assertion not allowed as child of sp:” + m_elementName,
                marker);
        }
    }
    public void addGeneral(AssertionBase asser, ValidationContext vctx) {
        if (asser instanceof TokenProperty) {
            TokenProperty token = (TokenProperty)asser;
            String name = token.getName();
            if (m_tokenRoles.contains(name)) {
                TokenProperty prior = m_roleTokens.get(name);
                if (prior == null) {
                    m_roleTokens.put(name, token);
                } else {
                    vctx.reportError(“Duplicate token “, asser);
                }
            } else {
                vctx.reportError(“Token not allowed as child of sp:” + m_elementName,
                    asser);
            }
        } else if (asser instanceof AlgorithmSuite) {
            …
        } else {
            vctx.reportError(“Assertion not allowed as child of sp:” + m_elementName,
                asser);
        }
    }
    public boolean complete(ValidationContext vctx) {
        if (m_algorithmSuite == null) {
            vctx.reportError(“Missing required sp:AlgorithmSuite property”, this);
            return false;
        } else {
            return true;
        }
    }
}
 
  清单 4 和 清单 5 都使用一个VerificationHandlerUtils.buildNameSet()方法创建一组字符串类型的名称集。这个方法使用 | 字符分隔输入字符串,然后查找名称,并将它添加到集合,这样的代码比单独传递名称更简洁一些。然后,这个名称集会用来检查嵌套策略允许的断言。

  解组(Unmarshalling)模型

  结合本质上相同的数据使用多名称空间会给XML数据绑定造成一些重大问题。大多数数据绑定工具只能通过为每一个名称空间创建单独的类来处理这里的多个名称空间。JiBX数据绑定的做法更好,它会在相同的类中使用多个绑定。每一个绑定都可以为每一个类使用相同的元素名称,但是使用不同名称空间。

  WS-Policy和WS-SecurityPolicy的宽松结构也会给数据绑定带来问题,但是同样,JiBX只需要增加少量工作就能够干净利落完成数据处理。JiBX支持使用用户扩展代码来解组(和整理)那些原本无法绑定到XML的数据结构。我使用了一些自定义的分解器来处理WS-Policy和WS-SecurityPolicy数据。可能其中最有意思的是OperatorUnmarshaller,它是用来解组各种WS-Policy操作符及其断言的。清单 6 显示了这个分解器的代码:

  清单 6. OperatorUnmarshaller    
 

以下是引用片段:
public class OperatorUnmarshaller implements IUnmarshaller, IAliasable {
    …
    public boolean isPresent(IUnmarshallingContext ictx) throws JiBXException {
        UnmarshallingContext ctx = (UnmarshallingContext)ictx;
        ctx.toTag();
        if (PolicyNamespace.LOOKUP.getNamespace(ctx.getElementNamespace()) != null) {
            String name = ctx.getElementName();
            return “Policy”.equals(name) || “ExactlyOne”.equals(name) ||
                “All”.equals(name);
        }
        return false;
    }
    
    public Object unmarshal(Object obj, IUnmarshallingContext ictx) … {
        if (isPresent(ictx)) {
            return unmarshalOperator((UnmarshallingContext)ictx);
        } else {
            return null;
        }
    }
    private OperatorBase unmarshalOperator(UnmarshallingContext ctx) … {
        
        // create the instance to be returned
        NamespaceInfo ns = PolicyNamespace.LOOKUP.
            getNamespace(ctx.getElementNamespace());
        if (ns == null) {
            throw new IllegalStateException(“Internal error – …”);
        }
        Policy policy = Policy.getNestedPolicy(ctx);
        PolicyNamespace prior = policy == null ?
            null : (PolicyNamespace)policy.checkNamespace(ns);
        Policy policy = Policy.getNestedPolicy(ctx);
        String name = ctx.getElementName();
        OperatorBase operator;
        if (“Policy”.equals(name)) {
            policy = new Policy(policy, ns);
            operator = policy;
        } else if (“ExactlyOne”.equals(name)) {
            operator = new OperatorBase.ExactlyOne(ns);
        } else {
            operator = new OperatorBase.All(ns);
        }
        
        // check for namespace conflict
        if (prior != null && ns != prior) {
            ((ValidationContext)ctx.getUserContext()).reportError(“Policy namespace ” +
                ns.getUri() + ” conflicts with containing policy namespace ” +
                prior.getUri(), operator);
        }
        
        // track object and handle all attributes
        ctx.pushTrackedObject(operator);
        operator.readAttributes(ctx);
        ctx.nextToken();
        
        // process all child elements
        while (ctx.toTag() == IXMLReader.START_TAG) {
            if (isPresent(ctx)) {
                
                // unmarshal child policy operator
                operator.getChildOperators().add(unmarshalOperator(ctx));
                
            } else {
                String uri = ctx.getElementNamespace();
                name = ctx.getElementName();
                IUnmarshaller unmarshaller = ctx.getUnmarshaller(uri, name);
                if (unmarshaller == null) {
                    
                    // treat unmapped element from known namespace as marker assertion
                    ns = policy.getNamespace(uri);
                    if (ns != null) {
                        operator.getChildAssertions().add
                            (ExtensibleMarkerUnmarshaller.unmarshal(ctx, ns));
                    } else {
                        // just use DOM for element from unknown namespace
                        …
                    }
                    
                } else {
                    
                    // unmarshal known child element as policy assertion
                    Object object = unmarshaller.unmarshal(null, ctx);
                    if (object instanceof AssertionBase) {
                        operator.getChildAssertions().add((AssertionBase)object);
                    } else {
                        throw new JiBXException(“Internal error – child element …”);
                    }
                    
                }
            }
        }
        ctx.nextToken();
        ctx.popObject();
        return operator;
    }
}

  IUnmarshaller接口只定义了两个方法:isPresent()是用来检查当前元素的开始标签是由解组程序处理的,而unmarshal()方法则是用来分解元素中的数据。在 清单 6 的代码中,isPresent()方法只是检查当前元素的名称空间是否与某个WS-Policy版本相匹配,然后再检查元素名称是否与三种策略操作符名称的任意一个匹配(Policy、ExactlyOne或All)。

  unmarshal()方法也很简单,但只是因为它将所有工作都委托给unmarshalOperator()方法处理。unmarshalOperator()假定您找到其中一种策略操作符元素,然后使用相应的WS-Policy名称空间(验证该操作符所使用的名称空间与包含<wsp:Policy>元素的名称空间相匹配)创建一个匹配操作符类的实例。然后,它会执行一个循环来分解所有子元素。下面是 4 种处理子元素的方法:

  如果子元素是另一个策略操作符,那么就递归调用 unmarshalOperator() 方法。

  如果这个元素有一个解组程序(表示绑定定义包含了该元素的一个映射定义),那么就调用该解组程序。

  如果元素名称空间被认为是一种策略扩展名称空间,那么要将它分解为一个空的标记器断言。

  否则,将它作为一个未分类扩展元素,并使用一个DOM表示。
 
  第三种方法意味着标记器元素不需要在JiBX绑定中定义,这有助于使绑定相对比较简单(也不需要单独的类,这有助于保持相对简单的数据结构)。但是,这些绑定一定要为所有的非标记器断言定义JiBX mapping定义,而且每个名称空间必须使用不同的绑定。清单 7 显示了顶级绑定,它包含了WS-Policy和WS-SecurityPolicy的常见抽象映射(与任何元素名称无关,因此可以在名称空间内重用):

  清单 7. 顶级解组绑定定义 
    

以下是引用片段:
<binding package=”com.sosnoski.ws” trim-whitespace=”true”
    value-style=”attribute” force-classes=”true” direction=”input” track-source=”true”>
  
  <include path=”in-policy-200409.xml”/>
  <include path=”in-policy-200702.xml”/>
  <include path=”in-secpolicy-200507.xml”/>
  <include path=”in-secpolicy-200702.xml”/>
  
  <!– Base marker element mapping –>
  <mapping class=”com.sosnoski.ws.policy.ExtensibleMarker” unmarshaller=
    “com.sosnoski.ws.secpolicy.SecurityPolicyNamespace$SecurityPolicyMarkerUnmarshaller”/>
  
  <!– Basic nested policy mapping –>
  <mapping abstract=”true” class=”com.sosnoski.ws.secpolicy.NestedPolicy”
      pre-set=”preset” ordered=”false” allow-repeats=”true”>
    <structure set-method=”setPolicy” usage=”optional”
        unmarshaller=”com.sosnoski.ws.policy.OperatorUnmarshaller”/>
    <structure type=”org.w3c.dom.Element” unmarshaller=”org.jibx.extras.DomElementMapper”
        set-method=”addExtension” usage=”optional”/>
  </mapping>
  …
  <!– Token base mapping –>
  <mapping abstract=”true” class=”com.sosnoski.ws.secpolicy.TokenBase”
      ordered=”false” allow-repeats=”true”>
    <structure map-as=”com.sosnoski.ws.secpolicy.NestedPolicy”/>
    <structure name=”Issuer” set-method=”setIssuer” usage=”optional”
         unmarshaller=”com.sosnoski.ws.policy.ExtensibleValueUnmarshaller”/>
    <structure name=”IssuerName” set-method=”setIssuerName” usage=”optional”
         unmarshaller=”com.sosnoski.ws.policy.ExtensibleValueUnmarshaller”/>
  </mapping>
  
  <!– Token property base mapping –>
  <mapping abstract=”true” class=”com.sosnoski.ws.secpolicy.TokenProperty”
      pre-set=”preset” ordered=”false” allow-repeats=”true”>
    <structure map-as=”com.sosnoski.ws.secpolicy.NestedPolicy”/>
  </mapping>
  
  <!– Base handling for protection specifications –>
  <mapping abstract=”true” class=”com.sosnoski.ws.secpolicy.ProtectParts”
      pre-set=”preset” ordered=”false” allow-repeats=”true”>
    <structure name=”Body” set-method=”setBody” usage=”optional”
        unmarshaller=”com.sosnoski.ws.secpolicy.SecurityMarkerUnmarshaller”/>
    <structure name=”Attachments” set-method=”setAttachments” usage=”optional”
        unmarshaller=”com.sosnoski.ws.secpolicy.SecurityMarkerUnmarshaller”/>
    <structure name=”Header” set-method=”addHeader” usage=”optional”
        unmarshaller=”com.sosnoski.ws.secpolicy.Header$HeaderUnmarshaller”/>
  </mapping>
  
</binding>

  清单8显示了清单7绑定中<include>元素所引用的一对与名称空间版本相关的绑定,其中一个是WS-Policy名称空间,另一个是WS-SecurityPolicy名称空间。这会将与名称空间无关的数据模型类与特定名称空间的元素名称相关联,同时将处理传递给具体的解组程序类(对于WS-Policy操作符元素,即 清单6的OperatorUnmarshaller 类),或者委托给 清单7绑定中的一个抽象映射。

  清单8. WS-Polic和WS-SecurityPolicy分解绑定定义 
    

以下是引用片段:
<binding value-style=”attribute” force-classes=”true” direction=”input” 
      track-source=”true”>
  <!– Make the recommendation namespace the default –>
  <namespace uri=”http://schemas.xmlsoap.org/ws/2004/09/policy”
      default=”elements” prefix=”wsp”/>
  
  <!– Define all supported policy elements –>
  <mapping name=”Policy” class=”com.sosnoski.ws.policy.Policy”
      unmarshaller=”com.sosnoski.ws.policy.OperatorUnmarshaller”/>
  <mapping name=”ExactlyOne” class=”com.sosnoski.ws.policy.OperatorBase$ExactlyOne”
      unmarshaller=”com.sosnoski.ws.policy.OperatorUnmarshaller”/>
  <mapping name=”All” class=”com.sosnoski.ws.policy.OperatorBase$All”
      unmarshaller=”com.sosnoski.ws.policy.OperatorUnmarshaller”/>
  <mapping class=”com.sosnoski.ws.policy.PolicyReference” name=”PolicyReference”>
    <structure map-as=”PolicyReference”/></mapping>
</binding>
<binding value-style=”attribute” force-classes=”true” direction=”input” 
      track-source=”true”>
  <!– Make the WS-SecurityPolicy 1.1 namespace the default –>
  <namespace uri=”http://schemas.xmlsoap.org/ws/2005/07/securitypolicy”
      default=”elements” prefix=”sp1″/>
  
  <!– Token variations –>
  <mapping name=”SecureConversationToken” 
      class=”com.sosnoski.ws.secpolicy.SecureConversationToken”>
    <structure map-as=”com.sosnoski.ws.secpolicy.TokenBase”/></mapping>
  <mapping name=”X509Token” class=”com.sosnoski.ws.secpolicy.X509Token”>
    <structure map-as=”com.sosnoski.ws.secpolicy.TokenBase”/></mapping>
  …
  <!– Token property variations –>
  <mapping name=”InitiatorToken” 
      class=”com.sosnoski.ws.secpolicy.TokenProperty$InitiatorToken”>
    <structure map-as=”com.sosnoski.ws.secpolicy.TokenProperty”/></mapping>
  <mapping name=”InitiatorSignatureToken” 
      class=”com.sosnoski.ws.secpolicy.TokenProperty$InitiatorSignatureToken”>
    <structure map-as=”com.sosnoski.ws.secpolicy.TokenProperty”/></mapping>
  <mapping name=”InitiatorEncryptionToken” 
      class=”com.sosnoski.ws.secpolicy.TokenProperty$InitiatorEncryptionToken”>
    <structure map-as=”com.sosnoski.ws.secpolicy.TokenProperty”/></mapping>
  <mapping name=”RecipientToken” 
      class=”com.sosnoski.ws.secpolicy.TokenProperty$RecipientToken”>
    <structure map-as=”com.sosnoski.ws.secpolicy.TokenProperty”/></mapping>
  <mapping name=”RecipientSignatureToken” 
      class=”com.sosnoski.ws.secpolicy.TokenProperty$RecipientSignatureToken”>
    <structure map-as=”com.sosnoski.ws.secpolicy.TokenProperty”/></mapping>
  <mapping name=”RecipientEncyrptionToken” 
      class=”com.sosnoski.ws.secpolicy.TokenProperty$RecipientEncyrptionToken”>
    <structure map-as=”com.sosnoski.ws.secpolicy.TokenProperty”/></mapping>
  <mapping name=”ProtectionToken” 
      class=”com.sosnoski.ws.secpolicy.TokenProperty$ProtectionToken”>
    <structure map-as=”com.sosnoski.ws.secpolicy.TokenProperty”/></mapping>
  <mapping name=”EncryptionToken” 
      class=”com.sosnoski.ws.secpolicy.TokenProperty$EncryptionToken”>
    <structure map-as=”com.sosnoski.ws.secpolicy.TokenProperty”/></mapping>
  <mapping name=”SignatureToken” 
      class=”com.sosnoski.ws.secpolicy.TokenProperty$SignatureToken”>
    <structure map-as=”com.sosnoski.ws.secpolicy.TokenProperty”/></mapping>
  …
  <!– Define other assertions containing nested policies –>
  <mapping name=”AlgorithmSuite” class=”com.sosnoski.ws.secpolicy.AlgorithmSuite”>
    <structure map-as=”com.sosnoski.ws.secpolicy.NestedPolicy”/></mapping>
  <mapping name=”AsymmetricBinding” class=”com.sosnoski.ws.secpolicy.AsymmetricBinding”>
    <structure map-as=”com.sosnoski.ws.secpolicy.NestedPolicy”/></mapping>
  <mapping name=”BootstrapPolicy” class=”com.sosnoski.ws.secpolicy.BootstrapPolicy”>
    <structure map-as=”com.sosnoski.ws.secpolicy.NestedPolicy”/></mapping>
  <mapping name=”Layout” class=”com.sosnoski.ws.secpolicy.Layout”>
    <structure map-as=”com.sosnoski.ws.secpolicy.NestedPolicy”/></mapping>
  <mapping name=”SymmetricBinding” class=”com.sosnoski.ws.secpolicy.SymmetricBinding”>
    <structure map-as=”com.sosnoski.ws.secpolicy.NestedPolicy”/></mapping>
  <mapping name=”Trust13″ class=”com.sosnoski.ws.secpolicy.Trust13″>
    <structure map-as=”com.sosnoski.ws.secpolicy.NestedPolicy”/></mapping>
  …
  <!– Other elements with specific handling –>
  <mapping name=”SignedParts” class=”com.sosnoski.ws.secpolicy.ProtectParts$SignedParts”
      factory=”com.sosnoski.ws.secpolicy.ProtectParts.newSignedParts”>
    <structure map-as=”com.sosnoski.ws.secpolicy.ProtectParts”/></mapping>
  <mapping name=”EncryptedParts” 
      class=”com.sosnoski.ws.secpolicy.ProtectParts$EncryptedParts”
      factory=”com.sosnoski.ws.secpolicy.ProtectParts.newEncryptedParts”>
    <structure map-as=”com.sosnoski.ws.secpolicy.ProtectParts”/></mapping>
  …
</binding>

  即使使用JiBX,分解WS-Policy和WS-SecurityPolicy文档也是非常复杂的。但是通过组合使用绑定定义和用户扩展代码,分解任务会比使用那些采用更严格XML处理方法的数据绑定工具更简单些。

  结束语

  在本文中,您了解了使WS-SecurityPolicy难以理解的一些问题,以及一些处理WS-SecurityPolicy文档时常见的错误。此外,您还看到了支持WS-Policy和WS-SecurityPolicy的Java数据模型基础,它支持WS-SecurityPolicy文档的验证,并且了解如何使用JiBX数据绑定来分解文档以创建这个模型。

我们一直都在努力坚持原创.......请不要一声不吭,就悄悄拿走。

我原创,你原创,我们的内容世界才会更加精彩!

【所有原创内容版权均属TechTarget,欢迎大家转发分享。但未经授权,严禁任何媒体(平面媒体、网络媒体、自媒体等)以及微信公众号复制、转载、摘编或以其他方式进行使用。】

微信公众号

TechTarget微信公众号二维码

TechTarget

官方微博

TechTarget中国官方微博二维码

TechTarget中国

相关推荐

  • 当业务流程遭遇软件服务

    要是驾校的教练去出演像《速度与激情》这样的动作电影,我敢打赌他们给人的印象肯定有所不同。有人说:“他们应该更快绑好安全带。”这种分离类似于当今的业务流程管理。

  • 企业数据建模三件事

    数据建模可能并不是IT人员优先考虑的学习话题。而且在这个问题上也不会有谁来责难,而且这个问题确实是非常深入的技术,还有那么点乏味。

  • 理解并使用IMS SOAP Web服务

    让我们从Web服务的角度来审视这一话题。为了通过简单对象访问协议(SOAP)支持Web服务,IMS需要三大部分的东西。首先是Rational Developer for System z……

  • 用WebSphere DataPower SOA Appliances重写SOAPAction报头

    IBM WebSphere DataPower通常被用于将一个SOAP/HTTP后端Web服务转换或调整为不同的Web服务接口。在这些情况中,几乎都使用中间逻辑来转换SOAP消息。