Web-сервисы Java: Моделирование и проверка WS-SecurityPolicy

Распространенные ошибки при работе с WS-SecurityPolicy и способы моделирования WS-SecurityPolicy на языке Java для целей проверки и преобразования

WS-SecurityPolicy позволяет определить настройки безопасности в рамках описания сервиса средствами языка Web Service Description Language (WSDL). Это мощный инструмент, но работа с документами WS-SecurityPolicy может быть затруднительной. Чтобы утверждения были эффективными, они должны быть правильно структурированы, а пространства имен должны соответствовать используемой версии. В этой статье мы рассмотрим распространенные ошибки при создании документов WS-SecurityPolicy и покажем, как смоделировать WS-Policy и WS-SecurityPolicy на языке Java™ в целях проверки и преобразования.

Денис Сосноски, консультант, Sosnoski Software Solutions, Inc.

Денис Сосноски (Dennis Sosnoski) - основатель и ведущий специалист консалтинговой компании по технологиям Java - Sosnoski Software Solutions, Inc., специализирующейся в обучении и консультировании по проблемам XML и Web-сервисов. Он имеет более чем 30-летний опыт работы в профессиональном проектировании ПО, специализируясь на серверных XML и Java-технологиях. Денис является основным разработчиком интегрированной системы с открытым программным кодом JiBX XML Data Binding, построенной на базе технологии классов Java и связанной системы Web-сервисов JibxSoap, также как и системы Web-сервисов Apache Axis2. Он также был одним их экспертов при разработке спецификаций JAX-WS 2.0.



28.03.2012

Об этом цикле статей

Web-сервисы ― важная функция технологии Java в корпоративных вычислениях. В этом цикле статей консультант по XML и Web-сервисам Денис Сосноски рассказывает об основных структурах и технологиях, ценных для Java-разработчиков, использующих Web-сервисы. Следите за статьями цикла, чтобы быть в курсе последних разработок в данной области и знать, как применять их в своих собственных проектах.

В предыдущих статьях этой серии мы рассказали, как использовать WS-SecurityPolicy в WSDL-описаниях сервисов для настройки процесса обработки функций WS-Security. Один недостаток WS-SecurityPolicy состоит в том, что при составлении политики легко допустить ошибку, например, используя утверждение в неположенном месте или смешивая разные версии утверждения в одном документе. Многие стеки Web-сервисов молча игнорируют такие ошибки, то есть политика не будет работать как следует, и вам придется всю ее прочесывать в поисках причины.

В первой части этой статьи мы рассмотрим причины трудности работы с WS-SecurityPolicy и наиболее распространенные ошибки. Вторая часть посвящена введению в модель данных WS-Policy и WS-SecurityPolicy, которая поддерживает проверку структуры документов, и иллюстрации того, как можно использовать инструмент привязки данных JiBX для демаршаллинга документов в модель.

WS-Policy в формате WSDL

Использование пространств имен

В этой статье используются:

  • префикс wsdl для представления пространства имен WSDL 1.1 http://schemas.xmlsoap.org/wsdl/;
  • префикс wsp для пространства имен http://schemas.xmlsoap.org/ws/2004/09/policy, используемого версией WS-Policy submission;
  • префикс sp для пространства имен http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702, используемого версией WS-SecurityPolicy 1.2.

В статье Освоение WS-Policy детально рассматривается большинство аспектов WS-Policy, в том числе, как прикреплять утверждения политики к компонентам WSDL 1.1. Вкратце, WS-Policy позволяет прикреплять политики к WSDL-определениям сервисов в нескольких различных точках, соответствующих сообщениям и группам сообщений. В WS-Policy используются следующие четыре уровня группирования сообщений.

  • Сообщение - политика применяется к конкретному сообщению (везде, где используется сообщение, если политика присоединяется посредством элемента <wsdl:message>, или при использовании конкретной операции, если она присоединена посредством определений входа/выхода/ошибки операции в любом из элементов <wsdl:portType> или <wsdl:binding>).
  • Операция: политика применяется ко всем случаям обмена сообщениями для конкретной операции (политика, присоединенная посредством элемента <wsdl:operation> внутри <wsdl:binding> или <wsdl:portType>).
  • Конечная точка: политика применяется ко всем случаям обмена сообщениями для конкретной привязки сервиса (политика, присоединенная посредством элемента <wsdl:port> или <wsdl:binding>) или для всех привязок сервиса на основе определенного типа порта (политика, присоединенная к этому типу порта <wsdl:portType>).
  • Сервис: политика применяется ко всем конечным точкам и всем операциям, связанным с сервисом (политика, присоединенная в элементе <wsdl:service>).

Политики, применяемые на одном и том же уровне группирования сообщений, наследуются более низкими уровнями, так что фактическая (или эффективная в терминах WS-Policy) политика, применяемая к каждому сообщению, есть сочетание всех политик, применяемых на уровнях сообщения, операции, конечной точки и сервиса. Таким образом, политика определяется не только самим сообщением, но и контекстом, в котором оно используется.

Способы присоединения WS-Policy

В статье Освоение WS-Policy я привел самый распространенный способ прикрепления утверждений WS-Policy к WSDL-компонентам: использование элемента <wsp:PolicyReference>. Этот тип привязки по ссылке удобен для большинства целей, но он не единственный. Вместо этого можно использовать специальный атрибут – wsp:PolicyURIs – в любом элементе WSDL, соответствующем одному из уровней группирования сообщений. Значение атрибута ― это список URI утверждений политики, которые должны применяться к элементу. Утверждения политики можно применить к элементу WSDL и просто посредством прямого вложения.

Выбор между этими способами привязки утверждений политики чаще всего произвольный. С некоторыми элементами WSDL, которые не допускают элементов расширения в соответствии с исходной WSDL-схемой, может использоваться атрибут wsp:PolicyURIs - но последняя WSDL-схема WS-I допускает наличие элемента расширения на каждый элемент WSDL, так что для большинства стеков Web-сервисов это не составляет проблемы. Использование элемента <wsp:PolicyReference> или атрибута wsp:PolicyURIs позволяет многократно применять утверждения политики, в том числе внешние (хотя не все стеки Web-сервисов поддерживают внешние ссылки).

Существует также способ прикрепления внешней политики с помощью элемента <WSP:PolicyAttachment>. Теоретически, этот подход позволяет связывать политики с сервисами, внешними по отношению к WSDL. Однако большинство стеков Web-сервисов не рассчитано на обработку такого рода вложений и ожидает, что информация политики будет либо указана ссылкой, либо непосредственно включена в 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-сервисов предоставляет графические инструменты, которые можно использовать для создания документов WS-SecurityPolicy из меню. (Стек с открытым исходным кодом Metro тоже содержит такой инструмент - в составе NetBeans.) Результат, полученный с помощью такого инструмента, скорее всего, будет нормально работать со стеком Web-сервисов, содержащим этот инструмент, но может не соответствовать практическим рекомендациям или даже вообще оказаться некорректным. Тем, кто не использует такой инструмент, хочет приспособить конфигурацию безопасности, не поддерживаемую инструментом, или просто желает создать "чистую" политику, придется мириться со сложностью WS-SecurityPolicy.

Я никогда не был большим поклонником инструментов настройки 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.

Как говорилось в статье Освоение 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>

Слабый намек

Определение схемы WS-SecurityPolicy в основном лишь намек на реализацию идеи определения XML-структуры. Текущая опубликованная версия содержит ошибку даже в одном из имен элементов (RequireAppiesTo, вместо RequireAppliesTo), и, по крайней мере, одну ошибку в типах значений (атрибут sp:Name элемента <sp:Header>, который должен быть простым именем, но в схеме определяется как полное имя). Эти ошибки показывают, что схема нечасто используется на практике (если вообще используется).

Документ политики из листинга 1 состоит в основном из чередующихся уровней элементов WS-Policy (в данном случае из одного элемента <wsp:Policy>) и элементов WS-SecurityPolicy (с префиксом sp). К сожалению, XML-схема не в состоянии представить структуру этого типа.Каждое определение схемы относится к одному пространству имен, и хотя она может указывать и использовать элементы, определенные в другом пространстве имен, она не в состоянии определить внутреннюю структуру этих элементов. Таким образом, определение схемы WS-Policy может определять общую структуру, а схема WS-SecurityPolicy ― элементы, предназначенные для использования в сочетании с этой структурой, но нет никакого способа определить взаимодействие между этими двумя наборами элементов.

Поскольку XML-схема не в состоянии определить структуру документов, в тексте стандарта WS-SecurityPolicy, чтобы показать целевую структуру документов, используется то же представление синтаксиса, что и в рекомендации WS-Policy. В листинге 2 приведен пример, взятый из стандарта, для элемента <sр:SymmetricBinding>, используемого в листинге 1.

Листинг 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 и проверки документов на соответствие стандарту, не удивительно, что одна из наиболее распространенных проблем при работе с документами заключается в том, что утверждения добавляются в неподходящем месте. Например, если переместить элемент <EncryptedParts> (в конце листинга 1) внутрь выражения Policy предыдущего элемента <SymmetricBinding>, то получится структура, не соответствующая стандарту WS-SecurityPolicy. Интерпретация такой неправильно структурированной политики не определена и будет зависеть от особенностей каждого стека Web-сервисов - она может правильно работать, но, скорее всего, будет несовместимой с другими стеками.

Стеки Web-сервисов могут сообщать пользователям об ошибках структуры, а могут и не сообщать. Я протестировал приведенный выше пример неуместного элемента EncryptedParts с последними версиями трех стеков Web-сервисов с открытым исходным кодом (Apache Axis2 1.5.4, Metro 2.1 и Apache CXF 2.3.3), которые оцениваются в предыдущих статьях этого цикла, и только Metro сообщил об ошибке и отказался работать. Axis2 и CXF приняли политику без жалоб, но работали без шифрования тела сообщений. такой скрытый отказ затрудняет диагностику проблем, вызванных ошибками структуры политики.

Конфликты версий

В WS-Policy и WS-SecurityPolicy особенно остро стоит проблема пространств имен. На стандартизацию WS-Policy и WS-SecurityPolicy ушли годы, и более ранние версии технологии стали широко использоваться еще до выпуска стандартов. Официальные стандарты в основном следуют XML-структуре более ранних версий, но используют разные пространства имен, чтобы было ясно, какой набор правил применяется для конкретного документа.

Большинство инструментов поддерживает как официальные рекомендации, так и более ранние версии WS-Policy и WS-SecurityPolicy, что позволяет использовать разные версии. (Axis2 является исключением, поддерживая в своей текущей версии 1.5.4 только версию WS-Policy submission.) Несмотря на эту гибкость, использование разных пространств имен в одном и том же документе может создать проблемы. Нет никаких причин сознательно смешивать пространства имен 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

Как говорилось в статье Освоение и моделирование WSDL 1.1, моделирование WS-Policy и WS-SecurityPolicy на языке Java поднимает совершенно новый набор проблем, отличный от тех, которые встречались при моделировании WSDL 1.1. Если основная проблема моделирования WSDL ― обеспечить возможность разных вариантов порядка расположения элементов для общего пользования, то WS-Policy и WS-SecurityPolicy вообще игнорируют порядок элементов, предлагая вместо этого рыхлую структуру, на которую налагается большое количество правил. Это означает, что ожидаемое содержание элемента <WSP:Policy> (или любых других вариаций оператора WS-Policy ) полностью зависит от контекста элемента. Листинг 1 демонстрирует это, показывая различные модели содержания почти всех 15 элементов <WSP:Policy>, присутствующих в документе.

Модель данных

Утверждения WS-SecurityPolicy в основном бывают двух типов: утверждения-маркеры (пустые элементы), которые одним своим присутствием указывают на некоторые качества или функции, и структурированные утверждения с вложенной политикой, содержащей другие утверждения. Конечно, главная сложность кроется именно в структурированных утверждениях.

Обработка структурированных утверждений строится на базовом классе модели NestedPolicy и связана с интерфейсом VerificationHandler, который показан в листинге 3.

Листинг 3. NestedPolicy и VerificationHandler
public abstract class NestedPolicy extends AssertionBase {
   /** Вложенное определение политики. */
   private Policy m_policy;

   /** Произвольные элементы расширения. */
   private List<Element> m_extensions = new ArrayList<Element>();
   ...
   /**
    * Создание экземпляра обработчика для набора утверждений.
    * 
    * @return instance
    */
   public abstract VerificationHandler createHandler();

   /**
    * Проверка правильности политики. При этом используется обработчик, 
    * предоставленный методом {@link #createHandler()}, для проверки всех 
    * утверждений вложенной политики в контексте данного утверждения.
    * 
    * @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
{
    /**
     * Добавление утверждение-маркера.
     * 
     * @param marker
     * @param vctx
     */
    public abstract void addMarker(ExtensibleMarker marker, ValidationContext vctx);

    /**
     * Добавление общего утверждения (не-маркера).
     * 
     * @param asser
     * @param vctx
     */
    public abstract void addGeneral(AssertionBase asser, ValidationContext vctx);

    /**
     * Проверка того, что утверждения, включенные в эту коллекцию, 
     * отвечают всем требованиям политики. Этот метод используется 
     * только при проверке всей политики (в случае использования альтернатив – 
     * для одной конкретной комбинации вариантов).
     * 
     * @param vctx
     * @return <code>true</code> если нет ошибок, <code>false</code>
     * в случае ошибки
     */
    boolean complete(ValidationContext vctx);
}

Все структурированные утверждения расширяют класс NestedPolicy, реализуя метод createHandler() для возврата экземпляра интерфейса VerificationHandler с учетом конкретной структуры утверждения. VerificationHandler предоставляет методы для накопления и проверки утверждений, содержащихся во вложенной политике, так что именно здесь осуществляется детальная обработка структурированного утверждения. Там где присутствуют альтернативные политики, метод NestedPolicy.verify() создает отдельный экземпляр VerificationHandler для каждой альтернативы.

В листинге 4 и листинге 5 приведен конкретный пример того, как это работает, в виде классов, используемых для представления и проверки утверждения SymmetricBinding. Класс SymmetricBinding, показанный в листинге 4, довольно прост, он всего лишь определяет внутренний класс, основанный на классе BindingAssertionHandler, показанном в листинге 5, для проверки обработки.

Листинг 4. SymmetricBinding
public class SymmetricBinding extends NestedPolicy {
    public VerificationHandler createHandler() {
        return new AssertionHandler();
    }

    private static class AssertionHandler extends BindingAssertionHandler
    {
        /** Содержит имя элемента. */
        private static final String ELEMENT_NAME = "SymmetricBinding";

        /** Имена допустимых элементов с ролью токенов. */
        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>.)

BindingAssertionHandler, показанный в листинге 5, это общая база для проверки всех трех видов утверждений привязки (транспортных, асимметричных и симметричных). Каждый из них определяет одно или несколько утверждений-маркеров, одну или несколько ролей токенов, требуемых для <sp:AlgorithmSuite>, и дополнительно <sp:Layout> (последние два – это утверждения с дочерними утверждениями-маркерами, но без вложенной политики).

Листинг 5. BindingAssertionHandler
public class BindingAssertionHandler extends VerificationHandlerBase {
    /** Имена элементов-маркеров, допустимые в &lt;TransportBinding>. */
    public static final Set<String> TRANSPORT_BINDING_MARKERS =
        VerificationHandlerUtils.buildNameSet("IncludeTimestamp");

    /** Имена элементов-маркеров, допустимые в ... или  &lt;SymmetricBinding>. */
    public static final Set<String> ENCRYPTION_BINDING_MARKERS =
        VerificationHandlerUtils.
        buildNameSet("IncludeTimestamp|...|OnlySignEntireHeadersAndBody");
    /** Фактическое имя элемента. */
    private final String m_elementName;

    /** Роли, допустимые для токенов. */
    private final Set<String> m_tokenRoles;

    /** Свойства токена для связывания. */
    private final Map<String,TokenProperty> m_roleTokens;

    /** Утверждения-маркеры, допустимые в политике. */
    private final Set<String> m_knownMarkers;

    /** Утверждения-маркеры в роли токена. */
    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)) {

            // Выдача предупреждения о дублирующем утверждении.
            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) ... {

        // создание возвращаемого экземпляра
        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);
        }

        // проверка на конфликт пространств имен 
        if (prior != null && ns != prior) {
            ((ValidationContext)ctx.getUserContext()).reportError("Policy namespace " +
                ns.getUri() + " conflicts with containing policy namespace " +
                prior.getUri(), operator);
        }

        // отслеживание объекта и обработка всех атрибутов
        ctx.pushTrackedObject(operator);
        operator.readAttributes(ctx);
        ctx.nextToken();

        // обработка всех дочерних элементов
        while (ctx.toTag() == IXMLReader.START_TAG) {
            if (isPresent(ctx)) {

                // демаршаллинг дочернего оператора политики
                operator.getChildOperators().add(unmarshalOperator(ctx));

            } else {
                String uri = ctx.getElementNamespace();
                name = ctx.getElementName();
                IUnmarshaller unmarshaller = ctx.getUnmarshaller(uri, name);
                if (unmarshaller == null) {

                    // обработка неинкапсулированных элементов из известного 
                    // пространства как утверждений-маркеров
                    ns = policy.getNamespace(uri);
                    if (ns != null) {
                        operator.getChildAssertions().add
                            (ExtensibleMarkerUnmarshaller.unmarshal(ctx, ns));
                    } else {
                        // применение DOM к элементу неизвестного пространства имен
                        ...
                    }

                } else {

                    // демаршаллинг известного дочернего элемента как утверждения политики
                    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>, если таковой имеется). Затем он выполняет цикл для демаршаллинга всех дочерних элементов.Существует четыре способа обработки дочерних элементов:

  • если это другой оператор политики, выполняется рекурсивный 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"/>

  <!-- Простое преобразование элемента-маркера -->
  <mapping class="com.sosnoski.ws.policy.ExtensibleMarker" unmarshaller=
    "com.sosnoski.ws.secpolicy.SecurityPolicyNamespace$SecurityPolicyMarkerUnmarshaller"/>

  <!-- Простое преобразование вложенной политики -->
  <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>
  ...
  <!-- Простое преобразование токена -->
  <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>

  <!-- Простое преобразование свойства токена -->
  <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>

  <!-- Простая обработка условий защиты -->
  <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 показана пара привязок, зависящих от версии пространства имен, на которые ссылаются элементы <include> из привязки листинга 7, одна для пространства имен WS-Policy, а другая для пространства имен WS-SecurityPolicy. Они связывают классы модели данных, не зависящие от версии пространства имен, с именами элементов в определенном пространстве имен и передают управление либо специальным классам демаршаллеров (в случае элементов оператора WS-Policy это класс OperatorUnmarshaller из листинга 6 ), либо одному из абстрактных преобразований из привязки, приведенной в листинге 7.

Листинг 8. Определения привязки демаршаллинга WS-Policy и WS-SecurityPolicy
<binding value-style="attribute" force-classes="true" direction="input" 
      track-source="true">

  <!-- Назначение рекомендованного пространства имен пространством имен по умолчанию -->
  <namespace uri="http://schemas.xmlsoap.org/ws/2004/09/policy"
      default="elements" prefix="wsp"/>

  <!-- Определение всех поддерживаемых элементов политики -->
  <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">

  <!-- Назначение WS-SecurityPolicy 1.1 пространством имен по умолчанию -->
  <namespace uri="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy"
      default="elements" prefix="sp1"/>

  <!-- Варианты токена -->
  <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>
  ...
  <!-- Варианты свойств токена -->
  <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>
  ...
  <!-- Определение других утверждений, содержащих вложенные политики -->
  <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>
  ...
  <!-- Другие элементы, требующие специальной обработки -->
  <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. Мы рассмотрели также основы модели данных Java для сочетания WS-Policy и WS-SecurityPolicy, которая поддерживает проверку документов WS-SecurityPolicy, и показали, как можно использовать инструмент привязки данных JiBX для демаршаллинга документов при построении модели.

В следующей статье мы завершим разработку инструмента для проверки и переформатирования документов WSDL и WS-Policy/WS-SecurityPolicy. В ней будет подробнее рассказано о том, как модель обрабатывает альтернативные политики, и о других аспектах проверки, а также о том, как инструмент привязки данных JiBX применяется к выходным документам, преобразованным в формат, соответствующий текущем версиям стандартов и практическим рекомендациям.

Ресурсы

Научиться

  • Оригинал статьи.
  • W3C Web Services Policy Working Group: группа, определяющая спецификацию WS-Policy, текущей версией которой является WS-Policy 1.5. Ее работа основана на версии WS-Policy 1.2 submission, предложении, разработанном группой заинтересованных компаний. Как версия WS-Policy 1.2 submission, так и официальная рекомендация WS-Policy 1.5 широко применяются (и по крайней мере один стек Web-сервисов, Axis2, в настоящее время поддерживает только версию submission).
  • OASIS Web Services Secure Exchange (WS-SX) TC: организация, ответственная за WS-SecurityPolicy и разработавшая текущую версию WS-SecurityPolicy 1.3 (которая опирается на более раннюю WS-SecurityPolicy 1.2 и расширяет ее, сохраняя пространство имен WS-SecurityPolicy 1.2 для общих элементов). Ее работа основана на версии WS-Policy 1.1 submission, еще одном предложении, разработанном группой заинтересованных компаний. Сегодня большинство приложений использует официальную рекомендацию WS-SecurityPolicy 1.2/1.3, хотя и версия WS-SecurityPolicy 1.1 submission по-прежнему широко используется.

Получить продукты и технологии

  • Apache Neethi: Neethi представляет собой один из нескольких инструментов с открытым исходным кодом для работы с WS-Policy. Как и большинство других доступных инструментов для работы с политиками, Neethi базируется на DOM-представлении XML. Он не поддерживает WS-SecurityPolicy или другие расширения WS-Policy напрямую, а рассчитан на расширение приложениями для обеспечения такой поддержки.
  • Инструмент привязки данных JiBX: выполняет преобразования XML-документы и моделями данных Java. Обеспечивает более высокую производительность и гибкость по сравнению с другими подходами к привязке данных, такими как JAXB, что делает его особенно полезным для сложных структур документов, которые не укладываются в простые определения схемы.

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Технология Java, SOA и web-сервисы
ArticleID=807210
ArticleTitle=Web-сервисы Java: Моделирование и проверка WS-SecurityPolicy
publish-date=03282012