当前位置: 首页 > 工具软件 > WS-Policy > 使用案例 >

建模和验证WS-SecurityPolicy

羊舌兴文
2023-12-01

您已经在本系列的较早文章中看到了如何在WSDL服务描述中使用WS-SecurityPolicy来配置WS-Security处理。 WS-SecurityPolicy的一个缺点是很容易在构建策略时出错,例如通过在错误的位置使用断言或在文档中混合断言版本。 许多Web服务堆栈都静默地忽略了这些类型的错误,这意味着您的策略无法按预期运行,并且您不得不困惑于策略以试图找到原因。

在本文的第一部分,您将了解为什么WS-SecurityPolicy可能难以使用,并了解常见WS-SecurityPolicy错误的原因。 第二部分介绍了用于WS-Policy和WS-SecurityPolicy的数据模型,该模型支持验证文档的结构,然后说明如何使用JiBX数据绑定将文档解组到模型。

WSDL中的WS-Policy

了解WS-Policy ”详细介绍了WS-Policy的大多数方面,包括如何将策略断言附加到WSDL 1.1组件。 简而言之,WS-Policy允许您在与消息和消息分组相对应的几个不同点将策略附加到WSDL服务定义。 WS-Policy使用的消息分组的四个级别是:

  • 消息 -策略适用于特定消息(如果通过<wsdl:message>元素附加了策略,或者通过特定操作中的输入/输出/故障定义附加到特定操作中,则该消息将在任何地方使用。 <wsdl:portType><wsdl:binding>元素)。
  • 操作 -策略适用于特定操作的所有消息交换(通过<wsdl:binding><wsdl:portType>内的<wsdl:operation>元素附加的策略)。
  • 端点 -策略适用于特定服务绑定的所有消息交换(通过<wsdl:port><wsdl:binding>附加的策略)或基于特定端口类型的所有服务绑定(附加到该<wsdl:portType> )。
  • 服务 -策略适用于与服务相关联的所有端点和所有操作(策略附加在<wsdl:service>元素上)。

在消息分组的一个级别上应用的策略由较低层继承,因此,应用于每个消息的实际(或有效 ,以WS-Policy术语)策略是应用于消息,操作,端点和所有消息的所有策略的结合。服务层。 因此,策略不仅取决于消息本身,还取决于使用消息的上下文。

WS-Policy附件技术

在“ 了解WS-Policy ”中,我介绍了将WS-Policy断言附加到WSDL组件的最常用方法:使用<wsp:PolicyReference>元素。 这种通过引用进行的附件在大多数情况下都很方便,但这并不是唯一的技术。 您可以在与消息分组级别之一相对应的任何WSDL元素上使用特殊属性wsp:PolicyURIs 。 该属性的值是将应用于该元素的策略声明URI的列表。 您还可以通过直接嵌入策略声明来将策略声明附加到WSDL元素。

在这些附加策略声明的不同方式中的选择大部分是任意的。 wsp:PolicyURIs属性可以与某些WSDL元素一起使用,这些元素不允许根据原始WSDL架构使用扩展元素—但是最近的WS-I WSDL架构允许在每个WSDL元素上使用扩展元素,因此对于大多数WSDL而言,这不是问题Web服务堆栈。 使用<wsp:PolicyReference>元素或wsp:PolicyURIs属性,您可以重用策略声明,包括外部策略声明(尽管并非所有Web服务堆栈都支持外部引用)。

还有一种使用<wsp:PolicyAttachment>元素将策略附加到外部的方法。 从理论上讲,这种方法允许您将策略与WSDL外部的服务相关联。 但是,大多数Web服务堆栈并未设置为处理这种类型的附件,而是期望策略信息被WSDL服务描述引用或直接包含在WSDL服务描述中。

<wsp:PolicyReference>允许您使用可选属性为引用的策略定义摘要算法和摘要值。 摘要为外部策略引用提供了一定的安全性,尽管它不能防止对引用本身进行修改。 <wsp:PolicyAttachment>更进一步,允许将WS-Security直接用于保护整个附件。

WS-SecurityPolicy附件限制

WS-SecurityPolicy指定消息分组级别,在该消息分组级别,某些类型的策略断言应附加到服务描述:

  • 安全绑定断言<sp:TransportBinding>仅在端点级别(因为传输用于访问端点); 在端点或操作级别上的<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服务堆栈应能够处理不同的附件级别-但最好尽可能遵循建议。

上面未列出的WS-SecurityPolicy断言嵌套在其他断言中,因此包含断言的位置控制它们的级别。

使用WS-SecurityPolicy

WS-SecurityPolicy使用复杂而混乱的结构,难以直接构造或修改。 大多数商业Web服务堆栈都提供GUI工具,可用于从选项菜单生成WS-SecurityPolicy文档。 (作为NetBeans的一部分,开源Metro堆栈也提供了这种类型的工具。)这种类型的工具的输出可能会与提供该工具的Web服务堆栈很好地配合,但可能不是最佳实践形式甚至完全正确。 如果您不使用这样的工具,想要定制该工具不支持的安全配置,或者只是要确保您拥有干净的策略,则必须直面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 Schema本身和WS-Policy结构的局限性。

如“ 了解WS-Policy ”中所讨论的,WS-Policy提供了一个简单的框架,用于定义和组合策略声明,声明本身由WS-SecurityPolicy等扩展定义,每个扩展都使用其自己的名称空间。 清单1给出了一个WS-SecurityPolicy文档的示例,其中WS-Policy元素以粗体显示:

清单1. WS-SecurityPolicy示例
<wsp:Policy wsu:Id="SecureConv"
   xmlns:wsu="http://.../oasis-200401-wss-wssecurity-utility-1.0.xsd"
   xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
   xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
  <wsap:UsingAddressing xmlns:wsap="http://www.w3.org/2006/05/addressing/wsdl"/>
  <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 Schema无法表示这种类型的结构。 每个模式定义都处理一个名称空间,尽管它可以引用和使用在另一个名称空间中定义的元素,但是它不能为这些元素定义子结构。 因此,WS-Policy的模式定义可以定义一个通用结构,而WS-SecurityPolicy模式可以定义旨在与该结构结合使用的元素,但是无法定义这两组元素之间的交互。

因为XML Schema无法定义文档的结构,所以WS-SecurityPolicy标准的文本使用与WS-Policy建议相同的语法表示形式来显示文档的预期结构。 清单2显示了从标准中抽取的样本,用于清单1中使用的<sp:SymmetricBinding>元素:

清单2. WS-SecurityPolicy语法示例
<sp:SymmetricBinding xmlns:sp="..." ... >
   <wsp:Policy xmlns:wsp="...">
     (
       <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中使用的语法对于大多数开发人员而言应该相当容易理解:使用括号定界分组, | 表示替代方案之间选择的字符,以及? 表示可选组件。 (完整语法还使用*表示零次或多次出现,使用+表示一次或多次出现。)但是,这不是XML语法的标准,因此无法直接使用它来验证实例文档。

结构错误

鉴于WS-SecurityPolicy的复杂性和根据标准检查文档的难度,使用文档中最常见的问题之一是在错误的位置添加断言也就不足为奇了。 例如,如果要在前面的<SymmetricBinding>元素的Policy内部移动<EncryptedParts>元素(在清单1末尾附近),您将拥有与WS-SecurityPolicy标准不匹配的结构。 对这种结构不正确的策略的解释将是不确定的,并且会在每个Web服务堆栈中产生异想天开的感觉-它可能按预期在一个Web服务堆栈上工作,但可能无法与其他堆栈互操作。

Web服务堆栈可能会也可能不会向用户报告结构错误。 当我尝试在上面的示例中使用三个开源Web服务堆栈(Apache Axis2 1.5.4,Metro 2.1和Apache CXF 2.3.3)的最新版本对EncryptedParts元素进行了错误放置时,在本系列的早期文章中对此进行了评估,只有Metro报告错误并拒绝操作。 Axis2和CXF都毫无保留地接受了该策略,但在未加密消息正文的情况下进行了操作。 这种类型的静默故障使很难诊断由策略结构错误引起的问题。

版本冲突

命名空间是WS-Policy和WS-SecurityPolicy的一个特别麻烦的问题。 WS-Policy和WS-SecurityPolicy的标准化历时数年,并且在该标准发布之前,该技术的早期版本已得到广泛使用。 官方标准大多遵循早期版本的XML结构,但是使用不同的名称空间,因此很清楚哪些规则集适用于特定文档。

大多数工具都支持官方建议以及WS-Policy和WS-SecurityPolicy的早期版本,从而可以互换使用这些版本。 (Axis2是例外,自当前1.5.4发行版起仅支持WS-Policy的提交版本。)尽管具有这种灵活性,但在单个文档中使用不同的名称空间仍会造成问题。 没有理由故意混合使用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进行建模提出了与WSDL 1.1建模所涉及的挑战完全不同的挑战,如“ 理解和建模WSDL 1.1 ”中所述。 尽管对WSDL建模的主要问题是允许通用的许多元素顺序变化,但是WS-Policy和WS-SecurityPolicy完全忽略了元素顺序,而是提供了一个松散的结构,其中施加了许多规则。 这意味着<wsp:Policy>元素的预期内容(或任何其他WS-Policy运算符变体)完全取决于元素的上下文。 清单1展示了这一点,并为文档中的几乎所有15个<wsp:Policy>元素提供了不同的内容模型。

资料模型

WS-SecurityPolicy断言主要有两种类型:标记断言(空元素),仅通过它们的存在指示某种质量或功能; 或结构化断言,以及包含其他断言的嵌套策略。 当然,结构化断言是复杂性所在。

结构化断言的处理建立在模型的NestedPolicy基类和关联的VerificationHandler接口的基础上,如清单3所示:

清单3. NestedPolicyVerificationHandler
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类定义一个内部类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是处理所有三种类型的绑定声明(传输,非对称和对称)的验证的通用基础。 它们每个都定义一个或多个标记断言,一个或多个令牌角色,必需的<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()从字符串值构造一组名称。 此方法在|处中断输入字符串。 (竖线)字符来查找要添加到集合中的单个名称,这比单独传递名称的代码要简洁得多。 然后使用名称集检查嵌套策略中允许的断言。

解组模型

将多个名称空间与基本相同的数据一起使用会给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()用于检查当前元素的开始标记是否由unmarshaller处理,以及unmarshal()用于从元素中解组数据。 在清单6的代码中, isPresent()方法仅检查当前元素名称空间是否与WS-Policy版本之一匹配,然后检查元素名称是否与三个策略操作符名称( PolicyExactlyOneAll )中的任何一个匹配。 。

unmarshal()方法也很简单,但这仅是因为它将所有工作委托给unmarshalOperator()方法。 unmarshalOperator()假定您位于policy-operator元素之一,并开始使用适当的WS-Policy名称空间创建匹配的操作符类的实例(验证此操作符上使用的名称空间是否与包含的操作符相匹配)。 <wsp:Policy>元素(如果有)。 然后,它执行一个循环以解组所有子元素。 有四种处理子元素的方法:

  • 如果子级是另一个策略运算符,则递归调用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-Policy和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数据绑定来解组文档以构建模型。

下一篇文章将总结用于验证和重新格式化WSDL和WS-Policy / WS-SecurityPolicy文档的工具的开发。 在其中,您将了解更多有关该模型如何处理策略替代方案以及验证的其他方面的信息,以及如何使用JiBX数据绑定输出转换为标准的当前版本和最佳实践格式的文档。


翻译自: https://www.ibm.com/developerworks/java/library/j-jws21/index.html

 类似资料: