Conteúdo


Java Web Services

WS-Security com CXF

Veja como usar o WS-Security com a pilha de serviços da Web do Apache CXF

Comments

Conteúdos da série:

Esse conteúdo é a parte # de # na série: Java Web Services

Fique ligado em conteúdos adicionais dessa série.

Esse conteúdo é parte da série:Java Web Services

Fique ligado em conteúdos adicionais dessa série.

Assim como as pilhas de serviços da Web do Axis2 e do Metro discutidas em artigos anteriores desta série, o Apache CXF (que você conheceu em "Introdução ao CXF") oferece suporte ao uso da tecnologia de extensão SOAP da WS-Security para fornecer uma faixa completa de funções de segurança para trocas de mensagens. De maneira semelhante à de outras pilhas, o CXF usa a WS-SecurityPolicy para configurar a manipulação da WS-Security (embora a configuração manual também seja possível).

A implementação do CXF da WS-Security é baseada na biblioteca de software livre WSS4J (consulte Recursos). É a mesma biblioteca usada pelo código Axis2 e, consequentemente, alguns dos detalhes de configuração da WS-Security são semelhantes entre as duas pilhas. Apesar disso, a camada do código que interpreta a WS-SecurityPolicy para configurar a WSS4J é diferente. No Axis2, é manipulada pelo módulo Rampart distribuído separadamente, considerando que no CXF é manipulada pela cxf-rt-ws-policy e pelos módulos cxf-rt-ws-security (incluídos no cxf-#.jar padrão, em que # é o número da versão).

Neste artigo, serão apresentados dois exemplos de configuração da manipulação da WS-Security no CXF. O primeiro é um UsernameToken simples que apenas agrupa um nome de usuário e senha de texto simples. O segundo exemplo assina e criptografa as mensagens, usando chaves e certificados X.409. Esses exemplos correspondem aos usados com o Axis2 na "WS-Security básica do Axis2" e com a "WS-Security com assinatura e criptografia do Axis2" e com o Metro na "WS-Security com Metro," logo, é possível comparar as técnicas para ver as diferenças entre as pilhas. Consulte Download para obter o código de exemplo deste artigo.

Configuração básica

As configurações de segurança da WS-SecurityPolicy detalham o processamento de segurança necessário para as mensagens que estão sendo trocadas entre um cliente e o serviço. Na maioria dos casos, a pilha de serviços da Web também exige algumas informações adicionais para aplicar a segurança a uma troca de mensagens. Por exemplo, uma WS-SecurityPolicy poderá exigir que o cliente assine mensagens de solicitação enviadas ao servidor, fornecendo irrecusabilidade para o serviço. Nesse caso, a pilha de serviços da Web precisará de uma maneira de identificar a chave privada específica usada na assinatura ao enviar uma mensagem ao serviço.

O Axis2 e o Metro usam extensões padrão da WS-SecurityPolicy para fornecer parâmetros de segurança desse tipo. Como a WS-SecurityPolicy normalmente é integrada em na descrição de um serviço WSDL, geralmente será necessário modificar o documento WSDL para adicionar esses detalhes (embora o Axis2 permita definir uma política diretamente no código do cliente, como alternativa). Essa necessidade de modificar o documento WSDL é incômoda e um pouco contrária ao objetivo do WSDL, que é servir como descrição de um serviço.

O CXF toma uma abordagem diferente — ou talvez devessem ser abordagens— diferentes, já que existem várias maneiras de configurar o CXF com os parâmetros necessários adicionados ao aplicar uma configuração da WS-SecurityPolicy às mensagens. No lado do cliente, é possível fazer isso diretamente no código do cliente ou usando um arquivo de configuração Spring XML. No lado do servidor, sempre será necessário usar um arquivo de configuração XML, apesar de poder escolher entre diferentes tipos de arquivos. Os exemplos neste artigo mostram como essas alternativas funcionam no cliente e no servidor.

UsernameToken no CXF

O UsernameToken oferece uma forma padronizada de representar um par de nome de usuário e senha com WS-Security. As informações de senha podem ser enviadas como texto simples (normalmente usado na produção quando combinado com criptografia Transport Layer Security [TLS] ou WS-Security, mas conveniente para teste) ou como um valor do hash. Além de ser útil para muitas aplicações que querem exigir autenticação direta, o UsernameToken é o formulário mais simples do recurso WS-Security e é um ótimo ponto de partida para exemplos.

Para implementar um exemplo de UsernameToken em texto simples no CXF, será necessário usar uma definição de serviço WSDL com a configuração adequada de WS-Policy/WS-SecurityPolicy incluída. A Listagem 1 mostra uma versão editada da mesma definição de serviço WSDL básica usada em "Introdução ao CXF." A Listagem 1 inclui informações da política que exigem o UsernameToken em pedidos do cliente para o servidor. A referência à política no <wsdl:binding> é mostrada em negrito, como a própria política.

Listagem 1. WSDL do UsernameToken de texto simples
<?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/">
  
  <!-- Policy for UsernameToken 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"
      xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
    <wsp:ExactlyOne>
      <wsp:All>
        <!-- Empty <TransportBinding/> element required due to bug in 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>

Existe uma diferença significativa entre o WSDL da Listagem 1 e o usado para o UsernameToken nos exemplos do Axis2 e do Metro. Essa versão inclui um elemento de <sp:TransportBinding/> vazio como parte da WS-SecurityPolicy, que é necessário devido a um erro no release 2.2.6 do CXF usado para este artigo. Sem um <sp:TransportBinding/> ou alguma forma de criptografia ou assinatura, a manipulação da WS-SecurityPolicy do CXF não pôde processar o UsernameToken. Esse erro deverá ser corrigido em versões do CXF superiores à 2.2.6.

A Listagem 1 WSDL diz a todos que queiram acessar o serviço o que precisa ser feito em termos de manipulação de segurança. Conforme mencionado anteriormente, geralmente é necessário fornecer parâmetros adicionais ao CXF para usar uma política. Nesse caso, esses parâmetros são o nome de usuário e a senha do código do cliente para usar ao enviar uma solicitação e uma maneira de validar um nome de usuário e senha no lado do servidor ao receber uma solicitação. A seguir mostrarei exemplos de como fornecer essas informações adicionais em cada lado da troca.

Uso do lado do cliente

A configuração do suporte à WS-Security no cliente CXF poderá ser manipulada dinamicamente no código do cliente ou estaticamente em arquivos de configuração. A Listagem 2 mostra um exemplo de configuração dinâmica de UsernameToken no código do cliente:

Listagem 2. Configuração dinâmica doUsernameToken no código do cliente
// create the client stub
CXFLibrary service = new CXFLibrary();
Library stub = service.getLibrary();

...
// set the username and password
Map ctx = ((BindingProvider)stub).getRequestContext();
ctx.put("ws-security.username", "libuser");
ctx.put("ws-security.password", "books");

Um cliente JAX-WS usa uma interface gerada por proxy para acessar um serviço. No código da Listagem 2, essa é a interface da Library. É possível criar uma instância da interface (chamada stub no código do exemplo) chamando um método na subclasse do javax.xml.ws.Service gerado — nesse caso, a classe do CXFService. Apesar de não ser refletido no API do código gerado, o JAX-WS garante que a instância da interface do proxy retornada sempre será uma subclasse da classe do javax.xml.ws.BindingProvider. Para configurar o CXF dinamicamente, será necessário usar essa digitação implícita e moldar o proxy para a classe do BindingProvider e acessar o mapa da propriedade do contexto da solicitação através dessa moldagem. A Listagem 2 mostra como é possível definir o nome de usuário e senha da manipulação da WS-Security no mapa dessa propriedade.

A configuração estática usa os mesmos valores da propriedade que a configuração dinâmica, apenas definidos de maneira diferente. O CXF procura por um arquivo de configuração no caminho de classe na inicialização e, se encontrar o arquivo, o usa para definir os valores da propriedade. Por padrão, o arquivo de configuração deverá ser nomeado cxf.xml e estar em um diretório raiz do caminho de classe (apesar de ser possível alterar esse padrão usando uma propriedade de sistema, cxf.config.file.url). Listagem 3 mostra um exemplo de arquivo cxf.xml (presente no código de download como cxf-username-client.xml), que pode ser usado no lugar da configuração dinâmica mostrada na Listagem 2:

Listagem 3. UsernameToken Configuração estática no 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>

A abordagem da configuração estática pode ser conveniente ao usar valores fixados para os parâmetros da WS-Security. É necessário ser cauteloso sobre o nome do arquivo de configuração e a localização no caminho de classe, já que o arquivo é opcional e o CXF operará sem reclamação se o arquivo não for encontrado (até falhar ao tentar usar a WS-Security sem os parâmetros necessários). Caso encontre problemas, verifique a saída da criação de registros do INFO-level pelo cliente. Uma mensagem INFO: Loaded configuration file cxf.xml. (ou outro nome de arquivo definido usando a cxf.config.file.url propriedade de sistema) deverá aparecer; se a mensagem não aparecer, o arquivo não foi encontrado e será necessário verificar o caminho de classe para encontrar a causa.

Uso no lado do servidor

No lado do servidor, será necessário usar um arquivo de configuração para os parâmetros da WS-Security. A maneira mais simples de fazer isso é adicionar as informações ao arquivo cxf.servlet.xml que define o terminal do serviço. A Listagem 4 mostra uma versão modificada do cxf-servlet.xml usado em "Introducing CXF," com as informações da WS-Security adicionadas mostradas em negrito (presente no código de download como server/etc/cxf-username-servlet.xml):

Listagem 4. cxf-servlet.xml com parâmetros de segurança adicionados
<?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>

A informação adicionada para esse caso de UsernameToken é somente uma classe de retorno de chamada de segurança. Essa é a mesma abordagem usada nos exemplos do Axis2 e do Metro, em que o código da WS-Security chama uma classe de retorno de chamada fornecida pelo usuário para implementar a interface do javax.security.auth.callback.CallbackHandler com as informações de nome de usuário e senha. A classe de retorno de chamada pode implementar qualquer tipo de manipulação que quiser usar para verificar a combinação de nome de usuário e senha, logo, é uma técnica que permite flexibilidade máxima.

A Listagem 5 mostra a classe de retorno de chamada usada pelo código do exemplo. Essa classe inclui manipulação do caso do UsernameToken verificar o nome de usuário e senha e no caso de uso de assinatura e criptografia (discutido na próxima seção principal deste artigo).

Listagem 5. Classe de retorno de chamada do servidor
**
 * Simple password callback handler. This just handles two cases: matching the username
 * and password, and providing the password used for access to the private key.
 */
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;
      }
    }
  }
}

Como alternativa para usar o cxf-servlet.xml padrão, é possível configurar um arquivo diferente para configuração do lado do servidor no arquivo web.xml do aplicativo da Web. Essa abordagem é um pouco mais complexa para implementar, já que será necessário especificar diretamente cada módulo do CXF que será usado no lado do servidor, mas permite inicialização mais rápida do aplicativo da Web. Consulte a página "Configuration" da documentação do CXF para obter mais detalhes, no tópico "Server configuration files".

Construindo e executando o código de exemplo

Antes de experimentar o código de exemplo, é necessário fazer o download e instalar uma versão atual do CXF em seu sistema (consulte Recursos). Também será necessário editar o arquivo build.properties no diretório raiz do download do código de exemplo descompactado para alterar o valor da propriedade cxf-home para o caminho da instalação do CXF. Se o teste for executado com um servidor em um sistema ou porta diferente, também será possível alterar o host-name e a host-port.

Para construir um aplicativo de exemplo usando o Ant build.xml fornecido, abra um console para o diretório raiz do código de download e digite ant. Primeiro, isso invocará a ferramenta WSDLToJava do CXF para gerar classes de serviço JAX-WS 2.x e classes de modelo de dados JAXB 2.x, em seguida, compilará o cliente e o servidor e, finalmente, empacotará o código do servidor como WAR. Assim, será possível implementar o arquivo cxf-usrename.war gerado no servidor de teste e, finalmente, digitar ant run no console para tentar executar o cliente de amostra. O cliente de amostra é executado através de uma sequência de várias solicitações ao servidor, imprimindo resultados resumidos para cada solicitação.

Assinando e criptografando em CXF

A simplicidade do

UsernameToken torna-o um bom ponto de partida, mas não é um uso típico da WS-Security. Com mais frequência, assinaturas ou criptografias ou ambos estarão envolvidos ao usar a WS-Security. A Listagem 6 mostra um exemplo editado do WSDL usando assinaturas e criptografia (baseadas nos exemplos do "WS-Security with Metro" e do anterior "Axis2 WS-Security signing and encryption." Consulte o artigo do Axis2 para obter uma discussão mais detalhada da assinatura e criptografia em geral e para obter detalhes sobre a geração e uso de certificados autoassinados para a WS-Security). As partes de política do WSDL são apresentadas em negrito.

Listagem 6. Assinatura/criptografia de 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/">
  
  <!-- 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-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>

A única diferença significativa entre o WSDL da Listagem 6 e o usado nos artigos anteriores é que a parte da WS-Policy/WS-SecurityPolicy foi movida para o início do WSDL, ao manter as versões mais recentes da definição do esquema do WSDL 1.1.

Usar pares de certificados chave para assinar e criptografar mensagens é mais complexo para configurar que o exemplo de UsernameToken simples. Será necessário identificar armazenamentos de chaves como fontes de chaves e certificados e também fornecer as senhas necessárias para acessar chaves nos armazenamentos. As informações do armazenamento deverão ser fornecidas por um arquivo .properties; a senha usada para acessar uma chave privada deverá ser fornecida por um retorno de chamada. A seguir, será mostrado como isso funciona para o cliente e para o servidor.

Uso do lado do cliente

Assim como no exemplo do UsernameToken, será possível configurar os parâmetros de segurança necessários para assinar e criptografar mensagens diretamente no código do cliente ou usando um arquivo de configuração cxf-client.xml. A Listagem 7 mostra um cxf-client.xml usado com essa finalidade (cxf-signencr-client.xml no código de exemplo do download):

Listagem 7. cxf-client.xml com parâmetros de assinatura e criptografia
<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>

O cxf-client.xml da Listagem 7 define dois pares de arquivos de propriedades e nomes de usuários, um par para uso no processamento da assinatura e o outro para uso no processamento da criptografia. Cada arquivo de propriedade identifica um armazenamento de chaves e fornece informações de acesso desse armazenamento. O valor de nome de usuário associado identifica a chave (da assinatura) ou certificado (da criptografia) dentro do armazenamento para ser usado no processamento. Nesse caso, o processamento da assinatura e o processamento da criptografia usam o mesmo armazenamento de chaves, que contém o certificado do servidor e a chave privada e o certificado do cliente. Como há somente um armazenamento, as duas propriedades fazem referência ao mesmo arquivo client-crypto.properties. Esse arquivo, que deverá estar presente em um diretório raiz do caminho de classe, é mostrado na Listagem 8:

Listagem 8. Arquivo 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

O arquivo de propriedades da Listagem 8 é usado pelo código da WS-Security do WSS4J subjacente para configurar o processamento de assinatura e criptografia. Identifica o "provedor" usado para manipular o processamento de assinatura e criptografia, o tipo de armazenamento de chaves, a senha do armazenamento de chaves e o arquivo de armazenamento de chaves (que deverá estar presente em um diretório raiz do caminho de classe).

Além das informações do armazenamento de chaves, o arquivo cxf-client.xml da Listagem 7 define um outro parâmetro do — ws-security.callback-handler, anteriormente visto no cxf-servlet.xml da Listagem 4. Como no exemplo anterior, o valor desse parâmetro deverá ser uma classe do manipulador de chamada de retorno de segurança. O código do WSS4J chamará uma instância dessa classe quando for necessário acessar a senha usada para fixar a chave privada do cliente no armazenamento de chaves. A implementação usada no código de exemplo é mostrada na Listagem 9:

Listagem 9. Classe de retorno de chamada do lado do cliente
/**
 * Simple password callback handler. This just checks if the password for the private key
 * is being requested, and if so sets that value.
 */
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) {

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

      }
    }
  }
}

Assim como no exemplo do UsernameToken, é possível configurar os parâmetros de segurança no código do cliente como alternativa para usar um arquivo cxf-client.xml. É possível até mesmo substituir o arquivo de propriedades da Listagem 8 por valores que você constrói em código, definindo um java.util.Properties como o valor da chave da ws-security.encryption.properties no contexto da solicitação. (Consulte o exemplo da Listagem 2 de definição das propriedades de nome de usuário e senha no contexto.)

Uso no lado do servidor

No lado do servidor, será necessário incluir basicamente os mesmos parâmetros de segurança fornecidos ao cliente no arquivo cxf-servlet.xml. A Listagem 10 mostra o cxf-servlet.xml usado no código do exemplo (em que é possível encontrá-lo como server/etc/cxf-signencr-servlet.xml), com os parâmetros da WS-Security adicionados mostrados em negrito:

Listagem 10. cxf-servlet.xml com parâmetros de segurança adicionados
<?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>

As principais diferenças das configurações do cliente são que essa versão do servidor não especifica um arquivo de propriedades da criptografia e a configuração de criptografia do nome de usuário é useReqSigCert. Esse valor é um nome especial reconhecido pelo WSS4J para indicar que o certificado do cliente usado para assinar a solicitação deverá ser usado para criptografar a resposta. Usar essa configuração permite ao código do servidor trabalhar com clientes múltiplos, cada um com seu próprio certificado.

O arquivo server-crypto.properties é essencialmente idêntico ao client-crypto.properties mostrado na Listagem 8. A classe de retorno de chamada do servidor é o mesmo que o do exemplo do UsernameToken, mostrado na Listagem 5.

Construindo e executando o código de exemplo

No exemplo de assinatura e criptografia, será necessário alterar o arquivo build.properties para usar o variant-name=signencr (ao invés do valor do username do exemplo do UsernameToken). Fora isso, siga as mesmas etapas de desenvolvimento que o exemplo do UsernameToken.

Se executar o cliente usando a versão 2.2.6 atual do CXF, será possível ver algumas saídas de registro no nível de WARNING, por exemplo WARNING: No assertion builder for type ... registered. Essas mensagens não indicam qualquer problema no código e provavelmente serão eliminadas em versões posteriores do CXF.

Conclusão

Neste artigo, foi mostrado como usar a WS-Security com o CXF. Assim como o Axis2 e o Metro, o CXF oferece suporte à WS-SecurityPolicy no WSDL como abordagem padrão da configuração da WS-Security. Dependendo das necessidades do aplicativo, é possível configurar os parâmetros de segurança adicionais necessários de várias maneiras, sem integrar informações de implementação no WSDL do serviço. Em relação a isso, o CXF é mais fácil e prático de usar com a WS-Security do que Axis2 e Metro.

O teste do código de exemplo deste artigo revelou um erro no CXF, que está sendo reparado. Esse erro faz a UsernamePolicy ser ignorada, a menos que alguma outra forma de processamento de segurança também seja exigida pela política. É difícil julgar a robustez da manipulação da WS-SecurityPolicy do CXF baseado nos exemplos simples usados neste artigo, mas o design parece funcionar e é provável que quanto mais pessoas usarem esse recurso relativamente novo do CXF, qualquer peculiaridade na implementação será resolvida com rapidez.

A próxima parte dos serviços da Web do Java continua com o CXF, dessa vez voltado ao desempenho. Veja como o desempenho do CXF se compara aos releases mais recentes do Axis2 e do Metro, para transferências simples de mensagens e com o WS-Security em uso.


Recursos para download


Temas relacionados


Comentários

Acesse ou registre-se para adicionar e acompanhar os comentários.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=80
Zone=Tecnologia Java, Software livre
ArticleID=481926
ArticleTitle=Java Web Services: WS-Security com CXF
publish-date=04092010