Web-сервисы Java: Работаем в CXF с WS-Security

Узнайте, как использовать WS-Security в стеке Web-сервисов Apache CXF

Стек Web-сервисов Apache CXF поддерживает технологию WS-Security, в том числе использование WS-SecurityPolicy для конфигурирования политик безопасности. CXF гибок в средствах для настройки параметров, используемых для работы с WS-Security; он поддерживает на стороне клиента возможности статического и динамического конфигурирования параметров. В этой статье цикла Java Web services Деннис Сосноски расскажет об использовании в CXF технологии WS-Security на примере простого использования токена UsernameToken, а также работы с цифровыми подписями и шифрованием сообщений.

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



19.12.2011

Как и в стеках Web-сервисов Axis2 и Metro, рассмотренных в предыдущих статьях этого цикла, Apache CXF (с которым мы познакомились в статье "Introducing CXF") поддерживает использование технологии WS-Security, добавляющей в SOAP широкий спектр функций обеспечения безопасного обмена сообщениями. По аналогии с другими стеками CXF использует WS-SecurityPolicy для настройки использования средств WS-Security (хотя также допустима и настройка вручную).

Реализация WS-Security в CXF основана на открытой библиотеке WSS4J (см. Ресурсы). Эта библиотека также используется в Axis2, поэтому эти два стека похожи в некоторых деталях конфигурирования WS-Security. Тем не менее уровень кода, интерпретирующий WS-SecurityPolicy для конфигурирования WSS4J, в этих стеках различается. В Axis2 за это отвечает отдельно распространяемый модуль Rampart, тогда как в CXF за это отвечают модули cxf-rt-ws-policy и cxf-rt-ws-security (входящие в архив standard cxf-#.jar, где # - это номер версии).

В этой статье мы рассмотрим два примера конфигурирования WS-Security в CXF. В первом примере мы рассмотрим использование простого токена UsernameToken, который просто оборачивает текстовые имя пользователя и пароль. Во втором примере мы покажем, как подписывать и шифровать сообщения с помощью сертификатов и ключей X.409. Эти примеры соответствуют примерам, использованным в статьях об Axis2: "Axis2 WS-Security basics" и "Axis2 WS-Security signing and encryption", а также в статье о Metro: "WS-Security with Metro"; чтобы можно было видеть, как в этих стеках различается работа с WS-Security. В разделе Загрузка можно загрузить код примеров этой статьи.

Основы конфигурации

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

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

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

И в Axis2, и в Metro для обеспечения подобных параметров безопасности используются индивидуальные расширения WS-SecurityPolicy. Так как политики WS-SecurityPolicy обычно встроены в WSDL-описание сервиса, эту информацию, как правило, приходится добавлять в WSDL-документ (хотя в качестве альтернативы Axis2 позволяет задавать политики безопасности непосредственно в коде клиента). Эта необходимость изменять WSDL-документ и тягостна, и в определенном смысле противоречит предназначению WSDL-документа, который должен служить описанием сервиса.

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


UsernameToken в CXF

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

Для реализации безопасности на основе UsernameToken в CXF необходимо добавить в WSDL-описание сервиса соответствующую конфигурацию WS-Policy/WS-SecurityPolicy. В листинге 1 показана отредактированная версия простого WSDL-описания сервиса, который мы использовали в статье "Introducing CXF". В листинге 1 в описание добавлена политика безопасности, требующая использования в запросах, передаваемых от клиента к серверу, токена 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/">
  
  <!-- Политика, предписывающая передачу в запросах от клиента к 
  серверу текстового пароля в токене UsernameToken -->
  <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"
      xmlns:sp=
"http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
    <wsp:ExactlyOne>
      <wsp:All>
        <!-- Пустой элемент <TransportBinding/> 
        здесь необходим в виду ошибки
        в коде CXF версии 2.2.6 -->
        <sp:TransportBinding/>
        <sp:SupportingTokens>
          <wsp:Policy>
            <sp:UsernameToken sp:
            IncludeToken=".../IncludeToken/AlwaysToRecipient"/>
          </wsp:Policy>
        </sp:SupportingTokens>
      </wsp:All>
    </wsp:ExactlyOne>
  </wsp:Policy>
  
  <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="CXFLibrary">

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

  </wsdl:service>


</wsdl:definitions>

Есть одно существенное различие между WSDL-описанием, показанным в листинге 1, и описанием, использованным для работы с UsernameToken в примерах для Axis2 и Metro. В этой версии частью WS-SecurityPolicy является пустой элемент <sp:TransportBinding/>. Это необходимо в связи с ошибкой, имеющейся в CXF версии 2.2.6, которую мы использовали в этой статье. В реализации WS-SecurityPolicy от CXF политика UsernameToken не работает без указания элемента <sp:TransportBinding/>, или какой-либо дополнительной формы шифрования или подписи. Эта ошибка должна быть исправлена в одной из следующих за 2.2.6 версий CXF.

Представленное в листинге 1 WSDL-описание сообщает всем, кто хочет воспользоваться сервисом, о том, какие средства обеспечения безопасности нужно использовать при работе. Как уже упоминалось ранее, обычно для того, чтобы использовать какую-либо политику безопасности, необходимо сообщить CXF дополнительные параметры. В данном случае такими параметрами являются имя пользователя и пароль, которые клиент будет использовать для отправки запросов, а также средства для проверки имени пользователя и пароля при получении запроса на стороне сервера. Далее мы рассмотрим примеры того, как можно предоставить обеим сторонам эту дополнительную информацию.

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

Настройку параметров работы клиента CXF с WS-Security можно осуществлять либо динамически в коде клиента, либо статически в конфигурационных файлах. В листинге 2 показан пример динамического конфигурирования UsernameToken в коде клиента:

Листинг 2. Динамическое конфигурирование UsernameToken в коде клиента
// создаем клиентский экземпляр интерфейса
CXFLibrary service = new CXFLibrary();
Library stub = service.getLibrary();

...
// задаем имя пользователя и пароль
Map ctx = ((BindingProvider)stub).getRequestContext();
ctx.put("ws-security.username", "libuser");
ctx.put("ws-security.password", "books");

Клиент JAX-WS использует для доступа к сервису сгенерированный прокси-интерфейс. В листинге 2 - это интерфейс Library. Мы создаем экземпляр интерфейса (в данном примере он называется stub), вызывая метод сгенерированного подкласса javax.xml.ws.Service, - в данном случае класса CXFService. Хотя это не отражено в API сгенерированного кода, но, тем не менее, JAX-WS гарантирует, что возвращенный экземпляр прокси-интерфейса непременно будет подклассом класса javax.xml.ws.BindingProvider. Для динамического конфигурирования CXF следует воспользоваться этой неявной типизацией и привести объект прокси к классу BindingProvider, который позволяет получить доступ к словарю атрибутов контекста запроса. В листинге 2 показано, как задать в этом словаре имя пользователя и пароль для использования в WS-Security.

При статическом конфигурировании используются значения тех же атрибутов, что и при динамическом конфигурировании, просто задаются они другим образом. CXF при запуске проверяет наличие конфигурационного файла в classpath, и, если файл найден, использует его для задания значений атрибутов. По умолчанию конфигурационный файл должен называться cxf.xml и находиться в корневой директории classpath (хотя это можно изменить с помощью системного атрибута cxf.config.file.url). В листинге 3 показан пример файла cxf.xml (в коде для загрузки он называется cxf-username-client.xml), который можно использовать вместо динамической конфигурации, показанной в листинге 2:

Листинг 3. Статическое конфигурирование UsernameToken в файле cxf.xml
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:jaxws="http://cxf.apache.org/jaxws"
   xsi:schemaLocation="http://www.springframework.org/schema/beans 
   http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
   http://cxf.apache.org/jaxws 
   http://cxf.apache.org/schemas/jaxws.xsd">

  <jaxws:client name="{http://ws.sosnoski.com/library/wsdl}library"
      createdFromAPI="true">
    <jaxws:properties>
      <entry key="ws-security.username" value="libuser"/>
      <entry key="ws-security.password" value="books"/>
    </jaxws:properties>
  </jaxws:client>
</beans>

Подход со статическим конфигурированием может быть удобен, когда вы используете постоянные значения параметров WS-Security. Однако следует внимательно относится к имени конфигурационного файла и его местоположению в classpath, так как этот файл не обязателен и CXF будет работать, даже если не найдет этот файл (пока не случится ошибки при попытке использовать WS-Security без необходимых параметров). Если вы столкнулись с проблемами, взгляните на сообщения, выводимые клиентом в журнал на уровне INFO. Если вы не видите сообщения INFO: Loaded configuration file cxf.xml (здесь также может быть другое имя файла, заданное посредством системного атрибута cxf.config.file.url), значит, файл не был найден и вам нужно проверить classpath, чтобы понять, почему это произошло.

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

На серверной стороне параметры WS-Security необходимо указывать в конфигурационном файле. Простейший способ сделать это - добавить информацию в файл cxf-servlet.xml, в котором определяется конечная точка сервиса. В листинге 4 показана модифицированная версия файла cxf-servlet.xml, использованного в статье "Introducing CXF"; информация, относящаяся к WS-Security, в нем выделена жирным шрифтом (в коде примера этот файл находится по пути server/etc/cxf-username-servlet.xml):

Листинг 4. Файл cxf-servlet.xml с добавленными параметрами безопасности
<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:jaxws="http://cxf.apache.org/jaxws"
    xmlns:soap="http://cxf.apache.org/bindings/soap"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
      http://cxf.apache.org/jaxws
      http://cxf.apache.org/schemas/jaxws.xsd">

  <jaxws:endpoint id="Processor"
      implementor="com.sosnoski.ws.library.cxf.CXFLibraryImpl"

      wsdlLocation="WEB-INF/wsdl/library-signencr.wsdl"
      address="/">

    <jaxws:properties>
      <entry key="ws-security.callback-handler"
          value="com.sosnoski.ws.library.cxf.ServerCallback"/>
    </jaxws:properties>

  </jaxws:endpoint>
</beans>

Здесь для настройки UsernameToken мы просто добавили класс, реализующий обработку обратного вызова. Тот же самый подход использовался в примерах для Axis2 и Metro - код WS-Security обращается к предоставляемому пользователем классу обратного вызова, реализующему интерфейс javax.security.auth.callback.CallbackHandler, передавая ему в качестве параметров имя пользователя и пароль. В данном классе может быть реализована какая угодно логика, поэтому эта техника предоставляет максимум гибкости в проверке предоставленных имени пользователя и пароля.

В листинге 5 показан класс обратного вызова, используемый в данном примере. Этот класс включает в себя проверку имени пользователя и пароля как при использовании токена UsernameToken, так и при использовании подписей и шифрования сообщений (которые мы обсудим в следующем разделе статьи).

Листинг 5. Серверный класс обратного вызова
**
 * Простой класс обратного вызова для работы с паролями. Он применяется 
 * в двух случаях: для проверки имени пользователя и пароля и для 
 * получения пароля, используемого для доступа к частному ключу.
 */
public class ServerCallback implements CallbackHandler {
  public void handle(Callback[] callbacks)
  throws IOException, UnsupportedCallbackException {
    for (int i = 0; i < callbacks.length; i++) {
      WSPasswordCallback pwcb = (WSPasswordCallback)callbacks[i];
      String id = pwcb.getIdentifier();
      switch (pwcb.getUsage()) {
          case WSPasswordCallback.USERNAME_TOKEN_UNKNOWN:

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

        case WSPasswordCallback.DECRYPT:
        case WSPasswordCallback.SIGNATURE:

              // used to retrieve password for private key
              if ("serverkey".equals(id)) {
                    pwcb.setPassword("serverpass");
                }
                break;
      }
    }
  }
}

В качестве альтернативы использованию стандартного файла cxf-servlet.xml можно указать в файле web.xml Web-приложения другой файл, который будет использоваться для конфигурирования на серверной стороне. Это немного более сложный для реализации подход, ввиду необходимости явно указывать каждый модуль CXF, который вы будете использовать на сервере, но зато он позволяет ускорить запуск Web-приложения. За более подробной информацией обращайтесь к странице "Configuration" раздела "Server configuration files" документации CXF.

Сборка и выполнение кода примера

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

Чтобы собрать приложение с помощью файла build.xml для Ant, откройте консоль в корневой директории кода приложения и выполните команду ant. Эта команда сначала запустит программу WSDLToJava, которая сгенерирует классы сервиса JAX-WS 2.x и классы модели данных JAXB 2.x, затем скомпилирует код клиента и сервера, и, наконец, упакует код сервера в WAR-архив. Затем можно развернуть сгенерированный файл cxf-library.war на тестовом сервере, и, набрав в консоли ant run, запустить клиент приложения. Клиент выполняет серию запросов к серверу, печатая краткую информацию о результате каждого запроса.


Подписываем и шифруем сообщения с помощью CXF

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

Листинг 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/">
  
  <!-- Политика для подписи и последующего шифрования всех сообщений.
  От клиента к серверу сообщения передаются вместе с сертификатом, а 
  обратно - только с цифровым отпечатком. -->
  <wsp:Policy wsu:Id="SignEncr"
      xmlns:wsu="http://docs.oasis-open.org/...-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=".../AlwaysToRecipient">
                  <wsp:Policy>
                    <sp:RequireThumbprintReference/>
                  </wsp:Policy>
                </sp:X509Token>
              </wsp:Policy>
            </sp:InitiatorToken>
            <sp:RecipientToken>
              <wsp:Policy>
                <sp:X509Token sp: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:types>
    ...
  </wsdl:types>

  <wsdl:message name="getBookRequest">
    <wsdl:part element="wns:getBook" name="parameters"/>
  </wsdl:message>
  ...
  
  <wsdl:portType name="Library">
    ...
  </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="CXFLibrary">

    <wsdl:port binding="wns:LibrarySoapBinding" name="library">
      <wsdlsoap:address location="http://localhost:8080/cxf-library-signencr"/>
    </wsdl:port>

  </wsdl:service>

</wsdl:definitions>

Единственным существенным различием между WSDL-описанием, показанным в листинге 6, и описаниями, использованными в предыдущих статьях, является то, что часть, относящаяся к WS-Policy/WS-SecurityPolicy, была перенесена в начало документа в соответствии с самыми последними версиями схемы WSDL 1.1.

Использование пар "частный ключ - сертификат" для подписывания и шифрования сообщений сложнее в настройке, чем простая политика UsernameToken. Для их использования нужно определить хранилища, в которых должны находиться ключи и сертификаты, а также предоставить пароли, необходимые для доступа к ключам из этих хранилищ. Информация о хранилище ключей должна быть представлена в файле .properties, а пароль для доступа к частным ключам должен быть предоставлен обработчиком обратного вызова. Далее мы рассмотрим использование этой политики, как на сервере, так и на клиенте.

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

Как и в примере с UsernameToken, параметры безопасности, необходимые для подписи и шифрования сообщений, можно конфигурировать либо непосредственно в коде клиента либо в конфигурационном файле cxf-client.xml. В листинге 7 показано, как это можно конфигурировать в файле cxf-client.xml (в коде примера - это файл cxf-signencr-client.xml):

Листинг 7. Настраиваем в файле cxf-client.xml параметры цифровой подписи и шифрования сообщений
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:jaxws="http://cxf.apache.org/jaxws"
   xsi:schemaLocation="http://www.springframework.org/schema/beans 
   http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
   http://cxf.apache.org/jaxws 
   http://cxf.apache.org/schemas/jaxws.xsd">

  <jaxws:client name="{http://ws.sosnoski.com/library/wsdl}library"
      createdFromAPI="true">
    <jaxws:properties>
      <entry key="ws-security.signature.properties"
          value="client-crypto.properties"/>
      <entry key="ws-security.signature.username" value="clientkey"/>
      <entry key="ws-security.encryption.properties"
          value="client-crypto.properties"/>
      <entry key="ws-security.encryption.username" value="serverkey"/>
      <entry key="ws-security.callback-handler"
          value="com.sosnoski.ws.library.cxf.ClientCallback"/>
    </jaxws:properties>
  </jaxws:client>

</beans>

В файле cxf-client.xml, показанном в листинге 7, определены две пары файлов .properties и имен пользователя. Первая пара предназначена для использования в цифровой подписи, а вторая - для шифрования сообщений. В каждом файле .properties определяется хранилище ключей и предоставляется пароль для доступа к этому хранилищу. Соответствующее значение имени пользователя идентифицирует ключ (для подписывания) или сертификат (для шифрования) внутри этого хранилища, которые следует использовать при обработке сообщений. В данном случае и для подписывания, и для шифрования используется одно и то же хранилище, в котором содержатся и сертификат сервера, и сертификат клиента, и его частный ключ. Так как в обоих случаях используется одно хранилище, то и файл атрибутов используется один и тот же: client-crypto.properties. Он должен находиться в корневой директории classpath. Этот файл показан в листинге 8:

Листинг 8. Файл client-crypto.properties
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin 
org.apache.ws.security.crypto.merlin.keystore.type=jks 
org.apache.ws.security.crypto.merlin.keystore.password=nosecret 
org.apache.ws.security.crypto.merlin.file=client.keystore

Показанный в листинге 8 файл используется библиотекой WSS4J WS-Security для конфигурирования работы с подписями и шифрованием. Он идентифицирует "поставщика", используемого для подписывания и шифрования, тип хранилища ключей, пароль к нему и сам файл хранилища (который должен находиться в корневой директории classpath).

Помимо информации о хранилище ключей, в показанном в листинге 7 файле cxf-client.xml определяется еще один параметр - ws-security.callback-handler, который мы ранее видели в файле cxf-servlet.xml из листинга 4. Как и в предыдущем примере, значением этого параметра должен быть класс обратного вызова. Код WSS4J будет обращаться к этому классу, когда ему будет нужно получить пароль для доступа к частному ключу клиента внутри хранилища. Пример этого класса показан в листинге 9:

Листинг 9. Клиентский класс обратного вызова
/**
 * Это простой обработчик для работы с паролями. Он просто 
 * проверяет, запрашивается ли пароль к частному ключу,
 * и, если запрашивается, задает его значение.
 */
public class ClientCallback implements CallbackHandler {
  public void handle(Callback[] callbacks) throws IOException {
    for (int i = 0; i < callbacks.length; i++) {
      WSPasswordCallback pwcb = (WSPasswordCallback)callbacks[i];
      String id = pwcb.getIdentifier();
      int usage = pwcb.getUsage();
      if (usage == WSPasswordCallback.DECRYPT || usage == WSPasswordCallback.SIGNATURE) {

         // используется для извлечения пароля к частному ключу
         if ("clientkey".equals(id)) {
             pwcb.setPassword("clientpass");
         }

      }
    }
  }
}

Также как в примере с UsernameToken, в качестве альтернативы файлу cxf-client.xml, параметры безопасности можно конфигурировать непосредственно в коде клиента. Можно даже заменить представленные в листинге 8 файлы .properties значениями, конструируемыми в коде. Это можно сделать, задав в контексте запроса ключу ws-security.encryption.properties значение java.util.Properties. (В листинге 2 показан пример задания имени пользователя и пароля в качестве атрибутов контекста запроса.)

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

На стороне сервера нужно включить в файл cxf-servlet.xml практически те же параметры безопасности, которые мы определяли в конфигурационном файле клиента. В листинге 10 показан пример модифицированного файла cxf-servlet.xml (в коде примера вы можете найти его по пути server/etc/cxf-signencr-servlet.xml), в котором параметры WS-Security выделены жирным шрифтом:

Листинг 10. Файл cxf-servlet.xml с добавленными параметрами безопасности
<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:jaxws="http://cxf.apache.org/jaxws"
    xmlns:soap="http://cxf.apache.org/bindings/soap"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
      http://cxf.apache.org/jaxws
      http://cxf.apache.org/schemas/jaxws.xsd">

  <jaxws:endpoint id="Processor"
      implementor="com.sosnoski.ws.library.cxf.CXFLibraryImpl"
      wsdlLocation="WEB-INF/wsdl/library-signencr.wsdl"
      address="/">

    <jaxws:properties>
      <entry key="ws-security.signature.properties" value="server-crypto.properties"/>
      <entry key="ws-security.signature.username" value="serverkey"/>
      <entry key="ws-security.encryption.username" value="useReqSigCert"/>
      <entry key="ws-security.callback-handler"
          value="com.sosnoski.ws.library.cxf.ServerCallback"/>
    </jaxws:properties>

  </jaxws:endpoint>
</beans>

Основным отличием от клиентских настроек является то, что здесь не указывается файл атрибутов для шифрования сообщений, а в имени пользователя, которое следует использовать при шифровании, содержится значение useReqSigCert. Это специальное значение, понимаемое WSS4J, которое обозначает, что сертификат клиента, использованный в подписи сообщения, должен использоваться для шифрования ответа. Использование этого параметра позволяет коду сервера работать с множеством клиентов, каждый из которых имеет свой собственный сертификат.

Файл server-crypto.properties в сущности идентичен файлу client-crypto.properties, показанному в листинге 8. Серверный класс обратного вызова используется точно такой же, как в примере работы с токеном UsernameToken, который показан в листинге 5.

Собираем и выполняем код примера

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

Если вы запустите клиент, используя CXF версии 2.2.6, вы увидите сообщения, выводимые на уровне журналирования WARNING, например "WARNING: No assertion builder for type ... registered". Эти сообщения не сигнализируют о какой-либо проблеме в коде и возможно будут устранены в следующих версиях CXF.


Заключение

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

При тестировании кода примера для этой статьи была обнаружена ошибка CXF, над исправлением которой сейчас работают. Эта ошибка заключается в игнорировании элемента UsernamePolicy в случае, если политикой безопасности не требуется помимо нее какая-нибудь еще форма обеспечения безопасности. Сложно судить о надежности работы CXF с WS-SecurityPolicy на основе использованных в данной статье простых примеров, однако архитектура системы выглядит разумной и вполне вероятно, что чем больше людей будут использовать эту относительно новую функциональность CXF, тем быстрее будут устранены все недочеты ее реализации.

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


Загрузка

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

Ресурсы

Научиться

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

  • Загрузите CXF.

Обсудить

Комментарии

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-сервисы, Open source
ArticleID=781732
ArticleTitle=Web-сервисы Java: Работаем в CXF с WS-Security
publish-date=12192011