Web-сервисы Java: WS-Security с Metro

Как использовать платформу Web-сервисов Metro для работы с WS-Security

Стек Web-сервисов Metro основан на эталонных реализациях стандартов JAXB 2.x и JAX-WS 2.x Java™, но также включает поддержку полного спектра технологий расширения SOAP WS-*. Эта статья продолжает цикл статей Дениса Сосновского о Web-сервисах Java и посвящена настройке и использованию WS-Security в Metro.

Денис Сосноски, консультант, 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.



20.01.2012

Как следует из статьи Введение в Metro, в основе платформы Web-сервисов Metro лежит эталонная реализация стандартов связывания с данными JAXB 2.x и Web-сервисов JAX-WS 2.x. Но сами по себе JAXB и JAX-WS обеспечивают лишь базовую поддержку Web-сервисов. JAX-WS не охватывает технологий WS-*, дополняющих SOAP средствами, необходимыми для работы в корпоративной среде, так что для поддержки этих технологий требуется добавить другие программные компоненты.

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

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

В Metro основной дополнительный компонент – это Web Services Interoperability Technologies (WSIT). WSIT представляет собой текущее воплощение того, что первоначально называлось проектом Tango – попыткой Sun добиться совместимости функций WS-*, включая обеспечение безопасности и надежного обмена сообщениями, с платформой Microsoft.NET. WSIT наделяет Metro поддержкой WS-SecurityPolicy, WS-Trust, WS-SecureConversation, WS-ReliableMessaging и многих других функций. Фактическая обработка времени выполнения WS-Security реализуется другим дополнительным компонентом, XML and WebServices Security Project (XWSS).

В этой статье показано, как использовать и настраивать WS-Security с Metro для автономного использования в качестве Web-приложения (вне сервера Glassfish). В разделе Загрузки приведен полный исходный код примеров из этой статьи, которые реализуют простой сервис управления библиотекой, уже использовавшийся в этом цикле.

Основы WSIT

WSIT отвечает за настройку Metro времени выполнения для приведения в соответствие сервиса спецификациям WS-Policy, включая расширения WS-Policy, такие как WS-SecurityPolicy. Помимо стандартных расширений WS-Policy, Metro использует в документах политики специальные расширения для настройки информации пользователя (например, места хранения ключей и паролей), необходимой для работы системы безопасности.

WSIT получает сведения о политике из описания сервиса на языке Web Services Description Language (WSDL). На клиентской стороне это может вызвать путаницу, потому что для настройки WSIT и для определения сервиса JAX-WS используются разные разные WSDL-описания. Как говорилось в статье Введение в Metro, WSDL-описание, используемое для настройки клиента JAX-WS, берется либо непосредственно из сервиса, либо из места, указанного при формировании кода JAX-WS. WSDL-описание, используемое WSIT, имеет фиксированное имя файла (хотя этот файл может использовать <wsdl:import> для ссылки на отдельный файл с полным WSDL-описанием) и всегда доступно через classpath.

На стороне сервера WSIT требует, чтобы WSDL-описание находилось в месте, указанном в файле конфигурации WEB-INF/sun-jaxws.xml (об этом говорилось в статье Введение в Metro). Предоставленное WSDL-описание должно включать специальные расширения для настройки информации пользователя в WSIT, но из версии WSDL, предоставляемой в ответ на запросы HTTP GET к конечной точке сервиса, эти специальные расширения удалены.

Специальные расширения, используемые для настройки данных пользователя WSIT, выглядят одинаково на стороне клиента и сервера, но различаются пространством имен XML для элементов расширения. На клиенте это пространство имен http://schemas.sun.com/2006/03/wss/client, а на сервере ― http://schemas.sun.com/2006/03/wss/server.


UsernameToken в Metro

В статье Основы стандарта Axis2 WS-Security рассматривалось использование WS-Security в Axis2/Rampart на простом примере UsernameToken. UsernameToken обеспечивает стандартный способ представления пары имени пользователя и пароля для WS-Security. Информация о пароле может передаваться как обычный текст (на практике это, как правило, используется только в сочетании с шифрованием Transport Layer Security [TLS] или WS-Security, но для тестирования удобно) или как хэш-значение.

Для реализации простого примера текстового UsernameToken в Metro необходимо использовать WSDL-определение сервиса с добавлением соответствующей конфигурации WS-Policy/WS-SecurityPolicy. В листинге 1 показана отредактированная версия того же простого WSDL-определения сервиса, которое используется в статье Введение в Metro, но на этот раз с информацией политики, которая требует UsernameToken в ответ на запросы клиента к серверу. Ссылка на политику в <wsdl:binding> показана жирным шрифтом, как и сама эта политика.

Листинг 1. Текстовый WSDL UsernameToken
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions targetNamespace=
"http://ws.sosnoski.com/library/wsdl"
    xmlns:wns="http://ws.sosnoski.com/library/wsdl"
    xmlns:tns="http://ws.sosnoski.com/library/types"
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/">
  <wsdl:types>
    ...
  </wsdl:types>

  <wsdl:message name="getBookRequest">
    <wsdl:part element="wns:getBook" name="parameters"/>
  </wsdl:message>
  ...

  <wsdl:portType name="Library">
    <wsdl:operation name="getBook">
      <wsdl:input message=
"wns:getBookRequest" name="getBookRequest"/>
      <wsdl:output message=
"wns:getBookResponse" name="getBookResponse"/>
    </wsdl:operation>
    ...
  </wsdl:portType>

  <wsdl:binding name="LibrarySoapBinding" type="wns:Library">

    <wsp:PolicyReference
 xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
        URI="#UsernameToken"/>

    <wsdlsoap:binding style="document"
        transport="http://schemas.xmlsoap.org/soap/http"/>
    <wsdl:operation name="getBook">
      <wsdlsoap:operation soapAction="urn:getBook"/>
      <wsdl:input name="getBookRequest">
        <wsdlsoap:body use="literal"/>
      </wsdl:input>
      <wsdl:output name="getBookResponse">
        <wsdlsoap:body use="literal"/>
      </wsdl:output>
    </wsdl:operation>
    ...
  </wsdl:binding>

  <wsdl:service name="MetroLibrary">
    <wsdl:port binding="wns:LibrarySoapBinding" name="library">
      <wsdlsoap:address location
="http://localhost:8080/metro-library-username"/>
    </wsdl:port>
  </wsdl:service>

  <!-- Policy for Username Token
 with plaintext password, sent from client to
    server only -->
  <wsp:Policy wsu:Id="UsernameToken" xmlns:wsu=
     "http://docs.oasis-open.org/wss/2004/01/
oasis-200401-wss-wssecurity-utility-1.0.xsd"
     xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
    <wsp:ExactlyOne>
      <wsp:All>
        <sp:SupportingTokens
            xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
          <wsp:Policy>
            <sp:UsernameToken sp:IncludeToken=
".../IncludeToken/AlwaysToRecipient"/>
          </wsp:Policy>
        </sp:SupportingTokens>
      </wsp:All>
    </wsp:ExactlyOne>
  </wsp:Policy>

</wsdl:definitions>

WSDL из листинга 1 указывает любому, кто хочет получить доступ к сервису, что необходимо сделать с точки зрения обработки безопасности. К информации политики с обеих сторон, клиента и сервера, необходимо также добавить специальные расширения WSIT с деталями о настройках пользователя, чтобы указать, как будет осуществляться обработка безопасности. Эти спецрасширения вписываются в компоненты <wsp:Policy> WSDL-определения. Далее я приведу примеры этих расширений для каждой стороны.

Со стороны клиента

На клиентской стороне для настройки WSIT используется файл с (фиксированным) именем wsit-client.xml. Этот файл должен находиться в корневом каталоге (вне каких-либо пакетов) classpath или в подкаталоге META-INF каталога, указанного в classpath. Причем wsit-client.xml должен быть WSDL-документом, который либо непосредственно обеспечивает весь сервис WSDL, либо содержит ссылку на отдельное определение WSDL-сервиса с применением <wsdl:import>. В любом случае WSDL должен включать как полные требования WS-Policy/WS-SecurityPolicy, так и расширения конфигурации WSIT.

В листинге 2 показан раздел политики определения WSDL из листинга 1 со спецрасширением WSIT для настройки поддержки UsernameToken со стороны клиента. В данном случае это спецрасширение представляет собой элемент <wssc:CallbackHandlerConfiguration> и дочерние элементы, выделенные жирным шрифтом. Два дочерних элемента <wssc:CallbackHandler> определяют классы обратного вызова, первый для имени пользователя (имя="usernameHandler"), а второй ― для пароля (name="passwordHandler"). Указанные классы должны реализовывать интерфейс javax.security.auth.callback.CallbackHandler.

Листинг 2. Политика UsernameToken с расширениями WSIT со стороны клиента
<wsp:Policy xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsu=
    "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
    wsu:Id="UsernameToken">
  <wsp:ExactlyOne>
    <wsp:All>
      <sp:SupportingTokens
          xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
        <wsp:Policy>
          <sp:UsernameToken sp:IncludeToken=".../IncludeToken/AlwaysToRecipient"/>
        </wsp:Policy>
      </sp:SupportingTokens>
      <wssc:CallbackHandlerConfiguration wspp:visibility="private"
          xmlns:wssc="http://schemas.sun.com/2006/03/wss/client"
          xmlns:wspp="http://java.sun.com/xml/ns/wsit/policy">
        <wssc:CallbackHandler name="usernameHandler"
            classname="com.sosnoski.ws.library.metro.UserPassCallbackHandler"/>
        <wssc:CallbackHandler name="passwordHandler"
            classname="com.sosnoski.ws.library.metro.UserPassCallbackHandler"/>
      </wssc:CallbackHandlerConfiguration>
    </wsp:All>
  </wsp:ExactlyOne>
</wsp:Policy>

В листинге 2 оба обратных вызова используют один и тот же класс. В листинге 3 показан код класса обратного вызова, который просто проверяет тип каждого запроса обратного вызова и устанавливает соответствующее значение:

Листинг 3. Код обратного вызова со стороны клиента
public class UserPassCallbackHandler implements CallbackHandler
{
    public void handle(Callback[] callbacks) throws UnsupportedCallbackException {
        for (int i = 0; i < callbacks.length; i++) {
            if (callbacks[i] instanceof NameCallback) {
                ((NameCallback)callbacks[i]).setName("libuser");
            } else if (callbacks[i] instanceof PasswordCallback) {
                ((PasswordCallback)callbacks[i]).setPassword("books".toCharArray());
            } else {
                throw new UnsupportedCallbackException(callbacks[i],
                    "Unsupported callback type");
            }
        }
    }
}

Использовать обратный вызов для имени пользователя или пароля не обязательно. Если использовать фиксированные значения, то их можно задать непосредственно в соответствующих элементах <wssc:CallbackHandler>, заменив атрибуты на default="yyy" (где значение атрибута — фактическое имя пользователя или пароль).

Со стороны сервера

На стороне сервера данные конфигурации WSIT должны присутствовать в определении сервиса WSDL. Как сказано в статье Введение в Metro, местоположение WSDL-определения сервиса можно задать в качестве параметра в WEB-INF/sun-jaxws.xml в рамках его WAR-файла. Если функция WSIT не используется, это WSDL-определение необязательно; в этом случае оно будет сгенерировано автоматически на этапе выполнения. При использовании функций WSIT WSDL-определение обязательно и должно содержать любые элементы спецрасширения, необходимые для настройки WSIT для функций, используемых сервисом. В листинге 4 показан раздел политики из WSDL-определения из листинга 1 , но на этот раз со спецрасширением WSIT для настройки поддержки UsernameToken со стороны сервера.

Листинг 4. Политика UsernameToken с расширениями WSIT со стороны сервера
<wsp:Policy xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsu=
    "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
    wsu:Id="UsernameToken">
  <wsp:ExactlyOne>
    <wsp:All>
      <sp:SupportingTokens
          xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
        <wsp:Policy>
          <sp:UsernameToken sp:IncludeToken=".../IncludeToken/AlwaysToRecipient"/>
        </wsp:Policy>
      </sp:SupportingTokens>
      <wsss:ValidatorConfiguration wspp:visibility="private"
          xmlns:wsss="http://schemas.sun.com/2006/03/wss/server"
          xmlns:wspp="http://java.sun.com/xml/ns/wsit/policy">
        <wsss:Validator name="usernameValidator"
            classname="com.sosnoski.ws.library.metro.PasswordValidator"/>
      </wsss:ValidatorConfiguration>
    </wsp:All>
  </wsp:ExactlyOne>
</wsp:Policy>

В листинге 4 расширение WSIT со стороны сервера принимает форму элемента <wsss:ValidatorConfiguration> и дочернего элемента <wsss:Validator>, определяя класс, который должен использоваться в качестве валидатора обратного вызова. В листинге 5 приведен код этого класса, который должен реализовать интерфейс com.sun.xml.wss.impl.callback.PasswordValidationCallback.PasswordValidator. В данном случае он просто сверяет представленные имя пользователя и пароль с фиксированными значениями, но вместо этого легко мог бы использовать поиск в базе данных или другой механизм.

Листинг 5. Код обратного вызова со стороны сервера
public class PasswordValidator implements PasswordValidationCallback.PasswordValidator
{
    public boolean validate(Request request) throws PasswordValidationException {
        PasswordValidationCallback.PlainTextPasswordRequest ptreq 
            = (PasswordValidationCallback.PlainTextPasswordRequest)request;
        return "libuser".equals(ptreq.getUsername()) &&
            "books".equals(ptreq.getPassword());
    }
}

Инструмент политики Metro

Метро/WSIT требует добавления в WSDL-определения сведений о конфигурации, как в случае Axis2/Rampart. В предыдущих статьях этого цикла, посвященных Axis2/Rampart, при необходимости сгенерировать модифицированные WSDL-определения в процессе сборки применялся специальной инструмент политики. Аналогичный инструмент, приспособленный к потребностям Metro/WSIT, включен в пример кода загрузки для этой статьи.

Этот инструмент - приложение com.sosnoski.ws.MergeTool из каталога mergetool примера. MergeTool объединяет данные в целевой документ XML, сопоставляя вложенные XML-элементы, чтобы найти данные для слияния и определить точку слияния в целевом документе. В файле build.xml примера приложения MergeTool используется для добавления сведений о конфигурации WSIT клиента или сервера в WSDL-определении сервиса. Вы можете использовать MergeTool и в своих собственных приложениях – файл mergetool/readme.txt содержит некоторые основные сведения по применению, и в предоставленной сборке приведены соответствующие примеры.

При отсутствии <wsss:ValidatorConfiguration> Metro будет использовать механизм авторизации из контейнера Web-приложения (Web-сервер, обеспечивающий поддержку сервлетов).

Сборка и запуск примера

Прежде чем попробовать выполнить код примера, нужно загрузить и установить на свою систему текущую версию Metro (код был проверен с версией 1.5) (см. раздел Ресурсы). Необходимо также изменить файл build.properties в корневом каталоге разархивированного кода примера из загрузки, заменив значение свойства metro-home на путь к своей установке Metro. Если вы собираетесь тестировать код с сервером, установленным на другой системе или работающим через другой порт, измените также имена host-name и host-port.

Чтобы собрать пример приложения с использованием прилагаемого файла Ant build.xml, откройте консоль из корневого каталога кода загрузки и наберите ant. Откроется инструмент JAX-WS wsimport (он входит в дистрибутив Metro), который скомпилирует клиент и сервер и упакует код сервера как WAR (в процессе сгенерировав отдельные версии WSDL-определения сервиса со сведениями о конфигурации клиента и сервера WSIT). Обратите внимание, что версия wsimport, входящая в Metro 1.5, выдаст предупредительное сообщение (из-за особенности обработки инструментом схем, встроенных в WSDL): src-resolve: Cannot resolve the name 'tns:BookInformation' to a(n) 'type definition' component.

Теперь можно установить созданный файл metro-library.war на свой опытный сервер и набрать на консоли ant run, чтобы попробовать запустить пример клиента. Пример выполняет последовательность из нескольких запросов к серверу, распечатывая краткие результаты по каждому запросу.


Подпись и шифрование в Metro

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

Листинг 6. Подпись/шифрование WSDL
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions targetNamespace=
"http://ws.sosnoski.com/library/wsdl"
    xmlns:wns="http://ws.sosnoski.com/library/wsdl"
    xmlns:tns="http://ws.sosnoski.com/library/types"
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/">
  <wsdl:types>
    ...
  </wsdl:types>

  <wsdl:message name="getBookRequest">
    <wsdl:part element="wns:getBook"
 name="parameters"/>
  </wsdl:message>
  ...

  <wsdl:portType name="Library">
    <wsdl:operation name="getBook">
      <wsdl:input message=
"wns:getBookRequest" name="getBookRequest"/>
      <wsdl:output message=
"wns:getBookResponse" name="getBookResponse"/>
    </wsdl:operation>
    ...
  </wsdl:portType>

  <wsdl:binding name=
"LibrarySoapBinding" type="wns:Library">

    <wsp:PolicyReference
 xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
        URI="#SignEncr"/>

    <wsdlsoap:binding style="document"
        transport="http://schemas.xmlsoap.org/soap/http"/>
    <wsdl:operation name="getBook">
      <wsdlsoap:operation soapAction="urn:getBook"/>
      <wsdl:input name="getBookRequest">
        <wsdlsoap:body use="literal"/>
      </wsdl:input>
      <wsdl:output name="getBookResponse">
        <wsdlsoap:body use="literal"/>
      </wsdl:output>
    </wsdl:operation>
    ...
  </wsdl:binding>

  <wsdl:service name="MetroLibrary">
    <wsdl:port binding=
"wns:LibrarySoapBinding" name="library">
      <wsdlsoap:address
 location="http://localhost:8080/metro-library-username"/>
    </wsdl:port>
  </wsdl:service>

  <!-- Policy for first signing and then
 encrypting all messages, with the certificate
   included in the message from client
 to server but only a thumbprint on messages from
   the server to the client. -->
  <wsp:Policy wsu:Id="SignEncr" xmlns:wsu=
     "http://docs.oasis-open.org/wss
/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
     xmlns:wsp="http://schemas.
xmlsoap.org/ws/2004/09/policy">
    <wsp:ExactlyOne>
      <wsp:All>
        <sp:AsymmetricBinding
            xmlns:sp=
"http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
          <wsp:Policy>
            <sp:InitiatorToken>
              <wsp:Policy>
                <sp:X509Token sp:IncludeToken=
".../IncludeToken/AlwaysToRecipient">
                  <!-- Added this policy component so 
Metro would work with the same
                   certificates (and key stores) 
used in the Axis2/Rampart example. -->
                  <wsp:Policy>
                    <sp:RequireThumbprintReference/>
                  </wsp:Policy>
                </sp:X509Token>
              </wsp:Policy>
            </sp:InitiatorToken>
            <sp:RecipientToken>
              <wsp:Policy>
                <sp:X509Token sp:IncludeToken
=".../IncludeToken/Never">
                  <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
            xmlns:sp=
"http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
          <sp:Body/>
        </sp:SignedParts>
        <sp:EncryptedParts
            xmlns:sp=
"http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
          <sp:Body/>
        </sp:EncryptedParts>
      </wsp:All>
    </wsp:ExactlyOne>
  </wsp:Policy>

</wsdl:definitions>

Единственное существенное различие между WSDL-определением из листинга 6 и тем, что используется в предыдущем примере Axis2/Rampart, состоит в добавлении в компонент <sp:InitiatorToken> политики, которая требует применения ссылки-отпечатка (thumbprint reference), когда в сообщение не включен сертификат X.509. Это дополнение необходимо из-за различий в том, как ссылки в Metro и Axis2 обрабатываются по умолчанию.

Когда клиент (на языке WS-SecurityPolicy ― инициатор) отправляет сообщение, сертификат X.509 клиента передается как часть этого сообщения (из-за атрибута sp:IncludeToken=".../IncludeToken/AlwaysToRecipient" элемента <sp:InitiatorToken/wsp:Policy/sp:X509Token>) и используется на сервере для проверки подписей. Когда сервер отвечает клиенту, он должен ссылаться на тот же сертификат клиента, который использовался при обработке шифрования. Если не указан никакой другой метод, Axis2/Rampart по умолчанию использует для идентификации сертификата ссылку-отпечаток. Метро/WSIT по умолчанию использует другой метод, называемый идентификатором предметного ключа (subject key identifier - SKI). В примере Axis2/Rampart применялась форма сертификатов, которая не поддерживает SKI, поэтому с метро/WSIT они по умолчанию не работают. Добавление элемента <sp:RequireThumbprintReference/> в политику указывает Metro/WSIT, что для сертификата надо использовать ссылку-отпечаток.

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

Как и в случае примера с UsernameToken, WSIT, для того чтобы сообщать дополнительные детали конфигурации, необходимы спецрасширения информационной политики со стороны клиента и со стороны сервера.

Со стороны клиента

В листинге 7 показаны спецрасширения WSIT, добавленные к политике WSDL для настройки обработки примера на стороне клиента. Эти спецрасширения, выделенные жирным шрифтом, настраивают хранилище ключей (содержащее секретный ключ клиента и соответствующий сертификат) и хранилище доверенных сертификатов (содержащее сертификат сервера), необходимые для подписи и шифрования.

Листинг 7. Политика подписи и шифрования с расширениями WSIT на стороне клиента
<wsp:Policy xmlns:wsp=
"http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsu=
"http://docs.oasis-open.org/wss/2004/01/
oasis-200401-wss-wssecurity-utility-1.0.xsd" 
wsu:Id="SignEncr">
  <wsp:ExactlyOne>
    <wsp:All>
      <sp:AsymmetricBinding xmlns:sp=
      "http://docs.oasis-open.org/
ws-sx/ws-securitypolicy/200702">
        <wsp:Policy>
          ...
        </wsp:Policy>
      </sp:AsymmetricBinding>
      <sp:SignedParts
          xmlns:sp=
"http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
        <sp:Body/>
      </sp:SignedParts>
      <sp:EncryptedParts
          xmlns:sp=
"http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
        <sp:Body/>
      </sp:EncryptedParts>

      <wssc:KeyStore alias="clientkey" keypass="clientpass"
          location="client.keystore" storepass="nosecret"
          xmlns:wspp=
"http://java.sun.com/xml/ns/wsit/policy" wspp:visibility="private"
          xmlns:wssc="http://schemas.sun.com/2006/03/wss/client"/>
      <wssc:TrustStore location="client.keystore" peeralias="serverkey"
          storepass=
"nosecret" xmlns:wspp="http://java.sun.com/xml/ns/wsit/policy"
          wspp:visibility="private"
          xmlns:wssc="http://schemas.sun.com/2006/03/wss/client"/>
    </wsp:All>
  </wsp:ExactlyOne>
</wsp:Policy>

Спецрасширения WSIT, приведенные в листинге 7, содержат все необходимые параметры для доступа к хранилищу ключей и хранилищу доверенных сертификатов (которые в данном случае представляют собой один и тот же файл), включая пароль, необходимый для доступа к секретному ключу клиента (атрибут keypass="clientpass" элемента <wssc:KeyStore>. Для передачи пароля можно использовать также функцию обратного вызова, как показано в следующем разделе.

Именованные хранилище ключей и хранилище доверенных сертификатов должны находиться в подкаталоге META-INF каталога, указанного в classpath. Для этих файлов можно использовать и абсолютные пути – вместо простых имен файлов, - что позволит находить их в любом постоянном месте файловой системы. (Помните, что для клиента WSDL-определение, включающее спецрасширения WSIT, должно использовать фиксированное имя wsit-client.xml и находиться либо в корневом каталоге classpath, либо в подкаталоге META-INF корневого каталога, указанного в classpath).

Со стороны сервера

Спецрасширения WSIT со стороны сервера, добавленные к WSDL, показаны в листинге 8 (опять же жирным шрифтом). В этом случае атрибут keypass элемента <wsss:KeyStore> указывает имя класса, а не фактическое значение пароля (как в примере обработки на стороне клиента из листинга 7). При таком подходе указанный класс должен реализовывать интерфейс javax.security.auth.callback.CallbackHandler и будет вызываться кодом WSIT, когда тому нужен доступ к паролю секретного ключа. Этот же метод можно использовать для указания имени класса вместо значения пароля для значений storepass.

Листинг 8. Политика подписи и шифрования с расширениями WSIT на стороне сервера
<wsp:Policy xmlns:wsp=
"http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsu=
"http://docs.oasis-open.org/wss/2004/01/
oasis-200401-wss-wssecurity-utility-1.0.xsd" 
wsu:Id="SignEncr">
  <wsp:ExactlyOne>
    <wsp:All>
      <sp:AsymmetricBinding xmlns:sp=
      "http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
        <wsp:Policy>
          ...
        </wsp:Policy>
      </sp:AsymmetricBinding>
      <sp:SignedParts
          xmlns:sp="http://docs.oasis-open.org/ws-sx/
ws-securitypolicy/200702">
        <sp:Body/>
      </sp:SignedParts>
      <sp:EncryptedParts
          xmlns:sp="http://docs.oasis-open.org/
ws-sx/ws-securitypolicy/200702">
        <sp:Body/>
      </sp:EncryptedParts>

      <wsss:KeyStore alias="serverkey"
          keypass="com.sosnoski.ws.library.metro.
KeystoreAccess"
          location="server.keystore" storepass="nosecret"
          xmlns:wspp="http://java.sun.com/xml/ns/wsit/policy" 
wspp:visibility="private"
          xmlns:wsss="http://schemas.sun.com/2006/03/wss/server"/>
      <wsss:TrustStore location="server.keystore" storepass="nosecret"
          xmlns:wspp="http://java.sun.com/xml/ns/wsit/policy" 
wspp:visibility="private"
  xmlns:wsss="http://schemas.sun.com/2006/03/wss/server"/>
    </wsp:All>
  </wsp:ExactlyOne>
</wsp:Policy>

В листинге 9 показана реализация интерфейса CallbackHandler, используемого для этого примера.

Листинг 9. Код обратного вызова пароля доступа к хранилищу ключей на сервере
public class KeystoreAccess implements CallbackHandler
{
    public void handle(
       Callback[] callbacks) throws IOException, 
UnsupportedCallbackException {
        for (int i = 0; i < callbacks.length; i++) {
            Callback callback = callbacks[i];
            if (callback instanceof PasswordCallback) {
                ((PasswordCallback)callback).
setPassword("serverpass".toCharArray());
            } else {
                throw new UnsupportedCallbackException
(callback, "unknown callback");
            }
        }
    }
}

Сборка и исполнение кода

Пример с подписью и шифрованием содержит те же шаги по сборке, что и пример UsernameToken, за исключением того, что нужно изменить файл build.properties, чтобы использовать variant-name=signencr (вместо значения username из примера UsernameToken).

Проблема совместимости

Если попробовать использовать клиент Axis2/Rampart с сервером Metro/WSIT (или наоборот), то можно столкнуться с проблемой, когда клиент пытается добавить книгу с дублирующим международным стандартным номером книги (ISBN). В этом случае сервис возвращает ошибку, вместо обычного ответного сообщения SOAP. Версии Axis2/Rampart 1.5.x правильно выполняют обычную обработку подписи и шифрования, требуемую в таком случае WSDL-определением, а Metro/WSIT 1.5 ― нет, что приводит к ошибке на стороне клиента. Это ошибка кода WSIT, которая должна быть исправлена в следующей версии Metro.

Если запустить тест с более ранней версией Axis2/Rampart, проблем, вероятно, не будет – потому что вплоть до версии 1.5 в Rampart присутствовала та же ошибка.


Metro: следующая остановка

Поддержка WS-SecurityPolicy в Metro WSIT допускает как прямую настройку параметров, таких как имена пользователей и пароли (включая пароли хранилища ключей и секретного ключа), так и использование обратных вызовов для получения этих значений по мере необходимости. Это позволяет также выбирать между обработкой авторизации контейнера сервлетов и своим собственным обратным вызовом для проверки комбинации имени пользователя и пароля на сервере. Благодаря такой гибкости Metro легко удовлетворяет потребности многих видов приложений. Удобно также, что Metro поддерживает WS-Security WSIT/XWSS как интегрированные компоненты, а не как отдельный компонент (со своими собственными циклами выпуска и, как правило, несоответствием между различными версиями основных компонентов), как в случае Axis2 и Rampart.

К недостаткам можно отнести скудную информацию по самостоятельному использованию Metro/WSIT и его прямой настройке (в отличие от использования в сочетании с сервером приложений Glassfish и NetBeans IDE). Многие необходимые возможности описаны только в блогах или почтовых рассылках (см. раздел Ресурсы).

В следующей статье цикла Web-сервисы Java мы продолжим рассмотрение Metro, на этот раз с упором на производительность. Мы сравним производительность Metro с Axis2 как при обмене простыми сообщениями, так и при использовании с WS-Security.


Загрузка

ОписаниеИмяРазмер
Исходный код для этой статьиj-jws10.zip38 КБ

Ресурсы

Комментарии

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=788580
ArticleTitle=Web-сервисы Java: WS-Security с Metro
publish-date=01202012