Java Web Services: WS-Security com Metro

Veja como tratar o WS-Security com a estrutura de serviços da Web do Metro

A pilha de serviços da Web do Metro é baseada nas implementações de referência dos padrões Java™ JAXB 2.x e JAX-WS 2.x, mas também inclui suporte para uma gama completa de tecnologias de extensão WS-* SOAP. Este artigo continua a série da coluna de Dennis Sosnoski sobre Java Web services com cobertura da configuração WS-Security e uso no Metro.

Dennis Sosnoski, Consultant, Sosnoski Software Solutions, Inc.

Author photoDennis Sosnoski é um consultor e instrutor especializado em XML e serviços da Web baseados em Java. Sua experiência em desenvolvimento de software profissional se estende por mais de 30 anos, sendo que nos últimos 10 focou tecnologias XML e Java do lado do servidor. Dennis é o desenvolvedor líder da estrutura de software livre JiBX XML Data Binding e a estrutura de serviços da Web associada JiBX/WS, assim como um committer na estrutura de serviços da Web Apache Axis2. Também foi um dos membros do Grupo de Especialistas para as especificações JAX-WS 2.0 e JAXB 2.0. O material para a série Serviços da Web Java é baseado nas aulas de treinamento de Dennis.



16/Dez/2009

Como vimos em "Introducing Metro", as implementações de referência dos padrões de ligação de dados JAXB 2.x e de serviços da Web JAX-WS 2.x estão no centro da estrutura de serviços da Web do Metro. Mas por si mesmos, JAXB e JAX-WS fornecem somente suporte a serviços básicos da Web. O JAX-WS não cobre as tecnologias WS-* que atualizam o SOAP para trabalhar em ambientes empresariais, portanto, outros componentes de software são necessários para adicionar suporte para essas tecnologias.

Sobre esta série

Os serviços de Web são uma parte crucial do papel desempenhado pela tecnologia Java na computação empresarial. Nesta série de artigos, o consultor de serviços de XML e Web Dennis Sosnoski cobre as principais estruturas e tecnologias importantes para desenvolvedores Java que utilizam serviços de Web. Acompanhe a série para manter-se informado com relação aos mais recentes desenvolvimentos da área e saber como utilizá-los para auxiliá-lo em seus projetos de programação.

No Metro, o principal componente agregado são as Tecnologias de Interoperabilidade de Serviços de Web (WSIT). WSIT é a encarnação atual do que foi originalmente conhecido como Projeto Tango — esforço da Sun para assegurar a interoperabilidade dos recursos WS-*, incluindo segurança e sistema de mensagens confiável, com a plataforma Microsoft .NET. O WSIT oferece ao Metro suporte para WS-SecurityPolicy, WS-Trust, WS-SecureConversation, WS-ReliableMessaging, e mais. A manipulação real do tempo de execução do WS-Security é implementada por outro componente agregado, o XML e WebServices Security Project (XWSS).

Este artigo mostra como usar e configurar o WS-Security com o Metro para uso independente como aplicativo da Web (fora do servidor Glassfish). Consulte a seção de Download para obter o código de origem completo para os exemplos de artigos, que implementam o serviço simples de gerenciamento de biblioteca usado anteriormente nesta série.

Fundamentos do WSIT

O WSIT é responsável pela configuração do tempo de execução do Metro para atender às especificações WS-Policy para um serviço, incluindo extensões WS-Policy como a WS-SecurityPolicy. Além das extensões padrão WS-Policy, o Metro também usa extensões personalizadas em documentos de política para configurar informações do usuário (como locais de armazenamento de chave e senhas) necessárias para implementar a manipulação de segurança.

O WSIT obtém as informações de política a partir de uma descrição de serviço Web Services Description Language (WSDL). No lado do cliente, isso pode ser confuso porque o WSDL usado para a configuração WSIT é separado do WSDL usado para definir o serviço para JAX-WS. Como discutido em "Introducing Metro", o WSDL usado para configurar o cliente JAX-WS é obtido diretamente do serviço ou de um local especificado quando você gera o código JAX-WS. O WSDL utilizado pelo WSIT tem um nome de arquivo fixo (embora esse arquivo possa usar um <wsdl:import> para fazer referência a um arquivo separado com o WSDL completo) e é sempre acessado a partir do caminho de classe.

No lado do servidor, o WSIT exige que um WSDL seja fornecido em um local especificado pelo arquivo de configuração WEB-INF/sun-jaxws.xml (discutido em "Introducing Metro"). O WSDL fornecido deve incluir as extensões personalizadas usadas para configurar as informações do usuário para WSIT, mas essas extensões personalizadas são excluídas da versão do WSDL fornecida em resposta às solicitações HTTP GET para o terminal em serviço.

As extensões personalizadas usadas para configurar as informações do usuário do WSIT são iguais no lado do cliente e do servidor, mas diferem no espaço para nome XML usado para os elementos de extensão. No cliente, esse espaço de nomes é http://schemas.sun.com/2006/03/wss/client; no servidor, esse espaço de nomes é http://schemas.sun.com/2006/03/wss/server.


Token do nome de usuário no Metro

"Axis2 WS-Security basics" introduziu WS-Security em Axis2/Rampart com o simples caso de um UsernameToken. O UsernameToken oferece uma forma padronizada de representar um par de nomes de usuário e senhas com WS-Security. As informações de senha podem ser enviadas como texto simples (normalmente usado na produção somente quando combinado com criptografia Transport Layer Security [TLS] ou WS-Security— mas conveniente para teste) ou como um valor do hash.

Para implementar um exemplo simples em texto simples de token do nome do usuário no Metro, é 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 básica de serviço WSDL usada em "Introducing Metro", desta vez incluindo informações da política para exigir o Token de nome de usuário em solicitações do cliente para o servidor. A referência à política no <wsdl:binding> é mostrada em negrito, assim como a política em si.

Listagem 1. Token do nome do usuário WSDL em 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/">
  <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>

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. Também é necessário adicionar extensões personalizadas WSIT às informações de política tanto no lado do cliente quanto no do servidor com detalhes de configuração do usuário para indicar como a manipulação de segurança será implementada. Essas extensões personalizadas se ajustam aos componentes <wsp:Policy> do WSDL. Em seguida, mostrarei exemplos dessas extensões para cada lado.

Uso do lado do cliente

No lado do cliente, um arquivo com o nome (fixo) de wist-client.xml é usado para a configuração WSIT. Esse arquivo deve residir em um diretório-raiz (fora de qualquer pacote) no caminho de classe, ou em um subdiretório META-INF de um diretório no caminho de classe. E wsit-client.xml deve ser um documento WSDL que oferece serviço WSDL completo diretamente ou que faça referência a uma definição de serviço WSDL separada usando um <wsdl:import>. De qualquer forma, o WSDL deve incluir requisitos WS-Policy/WS-SecurityPolicy completos e as extensões de configuração WSIT.

A Listagem 2 mostra a seção de política da Listagem 1 WSDL, com uma extensão personalizada WSIT adicionada para configurar o suporte ao Token do nome de usuário do lado do cliente. Nesse caso, essa extensão personalizada é o elemento <wssc:CallbackHandlerConfiguration> e elementos filhos, mostrados em negrito. Os dois elementos filhos <wssc:CallbackHandler> definem as classes de retorno de chamada, a primeira para o nome do usuário (name="usernameHandler") e a segunda para a senha (name="passwordHandler"). A(s) classe(s) especificada(s) deve implementar a interface javax.security.auth.callback.CallbackHandler.

Listagem 2. Política de token do nome do usuário com extensões WSIT do lado do cliente
<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>

Na Listagem 2, ambos os retornos de chamada usam a mesma classe. A Listagem 3 mostra o código de classe de retorno de chamada, que apenas verifica o tipo de cada solicitação de retorno de chamada e define o valor apropriado:

Listagem 3. Código de retorno de chamada do cliente
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");
            }
        }
    }
}

Não é necessário usar um retorno de chamada para nome de usuário ou senha. Se utilizar valores fixos para isso, é possível defini-los diretamente nos elementos <wssc:CallbackHandler> substituindo os atributos classname="xxx" por atributos default="yyy" (onde o valor do atributo é o nome de usuário ou senha real).

Uso no lado do servidor

No lado do servidor, as informações de configuração WSIT precisam estar presentes na definição de serviço WSDL. Como discutido em "Introducing Metro" o local de um serviço WSDL pode ser especificado como um parâmetro em WEB-INF/sun-jaxws.xml dentro do arquivo WAR de serviço. Esse WSDL é opcional se não estiver utilizando recursos WSIT, caso em que um WSDL será automaticamente gerado em tempo de execução. Se estiver utilizando recursos WSIT, o WSDL é necessário e deve incluir quaisquer elementos de extensão personalizada necessários para configurar WSIT para os recursos usados pelo serviço. A Listagem 4 mostra a seção de política da Listagem 1 WSDL, agora com uma extensão personalizada WSIT adicionada para configurar o suporte ao Token do nome de usuário do lado do servidor (mostrado em negrito):

Listagem 4. Política de token do nome do usuário com extensões WSIT do lado do servidor
<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>

A extensão WSIT do lado do servidor na Listagem 4 toma a forma de um elemento <wsss:ValidatorConfiguration> e elemento filho <wsss:Validator>, especificando a classe para ser usada como um retorno de chamada validador. A Listagem 5 mostra o código para esta classe, que deve implementar a interface com.sun.xml.wss.impl.callback.PasswordValidationCallback.PasswordValidator. Nesse caso, ele somente verifica o nome de usuário e senha fornecidos contra valores fixos, mas poderia facilmente usar um banco de dados para procurar outro mecanismo.

Listagem 5. Código de retorno de chamada do servidor
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());
    }
}

Ferramenta de política do Metro

O Metro/WSIT exige a adição de informações de configuração aos WSDLs, exatamente como com Axis2/Rampart. Os artigos anteriores nesta série cobrindo Axis2/Rampart usaram uma ferramenta especial de política durante o processo de criação para gerar WSDLs conforme necessário. Uma ferramenta semelhante, projetada em torno das necessidades do Metro/WSIT, está incluída no download do código de exemplo para este artigo.

Essa ferramenta é o aplicativo com.sosnoski.ws.MergeTool, no diretório mergetool do código de amostra. MergeTool funde dados em um documento XML de destino, correspondendo elementos XML aninhados para encontrar os dados a serem fundidos e para determinar o ponto de fusão no documento de destino. O build.xml do aplicativo de amostra utiliza o MergeTool para adicionar informações de configuração WSIT para o cliente ou servidor dentro do WSDL para o serviço. Também é possível usar o MergeTool em seus próprios aplicativos, se desejar — o arquivo mergetool/readme.txt contém algumas informações básicas de uso na compilação fornecida.

Se não fornecer um <wsss:ValidatorConfiguration>, o Metro usará o mecanismo de autorização fornecido pelo contêiner de aplicativo da Web (o servidor da Web fornecendo suporte ao servlet).

Construindo e executando o código da amostra

Antes de experimentar o código da amostra, é necessário fazer o download e instalar uma versão atual do Metro (o código foi testado com o release 1.5) em seu sistema (consultar Recursos). Também é necessário editar o arquivo build.properties no diretório-raiz do download do código da amostra descompactado para alterar o valor da propriedade metro-home para o caminho para sua instalação do Metro. O teste será feito com um servidor em um sistema ou porta diferente, também é possível querer alterar o nome do host e a porta do host.

Para construir um aplicativo de exemplo usando o Ant build.xml fornecido, abra um console para o diretório-raiz do código do download e digite ant. Isso primeiro invocará a ferramenta JAX-WS wsimport (incluída na distribuição do Metro), depois compilará o cliente e servidor, finalmente empacotará o código do servidor como um arquivo WAR (gerando versões separadas do serviço WSDL contendo informações de configuração do cliente e servidor WSIT no processo). Observe que a versão do wsimport incluída no Metro 1.5 emitirá uma mensagem de aviso (por causa de uma mudança no processamento da ferramenta dos esquemas integrados no WSDL): src-resolve: Não é possível solucionar o nome 'tns:BookInformation' para um componente de 'definição de tipo'.

É possível então distribuir o arquivo metro-library.war gerado para o 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 no Metro

A simplicidade do token do nome do usuário torna-o um bom ponto de partida, mas não é um uso típico do WS-Security. Mais frequentemente, serão usadas assinaturas ou criptografia, ou ambas. A Listagem 6 mostra um exemplo editado de WSDL usando assinaturas e criptografia (baseada em um exemplo do artigo "Axis2 WS-Security signing and encryption" — consulte esse artigo para uma descrição mais detalhada da assinatura e criptografia WS-Security em geral). As porões 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/">
  <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>

A única diferença significativa entre o WSDL da Listagem 6 e a usada no exemplo anterior de Axis2/Rampart é a adição de uma política ao componente <sp:InitiatorToken> para exigir o uso de uma referência de cópia do polegar quando o certificado X.509 não está incluído em uma mensagem. Essa adição é necessária devido às diferenças em como as referências são tratadas por padrão no Metro e Axis2.

Quando o cliente (o inicializador, no WS-SecurityPolicy-speak) envia uma mensagem, o certificado X.509 para o cliente é enviado como parte da mensagem (por causa do atributo sp:IncludeToken=".../IncludeToken/AlwaysToRecipient" no elemento <sp:InitiatorToken/wsp:Policy/sp:X509Token>) e usado pelo servidor para verificar as assinaturas. Quando o servidor responde ao cliente, ele precisa fazer referência ao mesmo certificado do cliente usado no processamento da criptografia. O padrão Axis2/Rampart é usar uma referência de cópia de polegar para identificação do certificado, se nenhum outro método for especificado. O padrão Metro/WSIT é usar outro método, chamado de identificador de chave de assunto (SKI). Os certificados usados no exemplo Axis2/Rampart foram de um formulário que não suporta o SKI, portanto não funcionarão com o Metro/WSIT por padrão. Adicionar o elemento <sp:RequireThumbprintReference/> à política, diz ao Metro/WSIT para usar uma referência de cópia de polegar ao invés do certificado.

Esta alteração na política permite que os mesmos certificados e armazéns de chave usados no exemplo Axis2/Rampart anterior sejam usados para este exemplo. Isso, por sua vez, permite que o exemplo do cliente Axis2/Rampart seja usado com o servidor do Metro/WSIT e vice versa, como uma forma conveniente de verificar a interoperabilidade. Se experimentar isso (o que pode ser feito alterando o caminho de destino passado para o cliente de teste em cada caso), descobrirá que para a maior parte as mensagens são trocadas sem dificuldade — mas com uma diferença na operação, discutida na seção problema de interoperabilidade abaixo.

Como com o exemplo do token do nome do usuário, o WSIT precisa de extensões personalizadas às informações da política no lado do cliente e do servidor para oferecer detalhes adicionais de configuração.

Uso do lado do cliente

A Listagem 7 mostra as extensões personalizadas WSIT adicionadas à política WSDL para configurar a manipulação do lado do cliente para o exemplo. Essas extensões personalizadas, mostradas em negrito, configuram o armazém da chave (contendo a chave particular do cliente e o certificado correspondente) e o armazém de confiança (contendo o certificado do servidor) necessários para assinar e criptografar.

Listagem 7. Política de assinatura e criptografia com extensões WSIT do lado do cliente
<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>

A Listagem 7 Extensões WSIT personalizadas, oferece todos os parâmetros necessários para acessar o armazém da chave e armazém de confiança (que nesse caso são o mesmo arquivo), incluindo a senha necessária para acessar a chave particular do cliente (o atributo keypass="clientpass" no elemento <wssc:KeyStore>). Também é possível usar um retorno de chamada para informação da senha, como será mostrado na próxima seção.

O armazém de chave e armazém de confiança nomeados devem estar em um diretório filho META-INF de um diretório no caminho da classe. Também é possível usar caminhos absolutos de arquivos para esses arquivos — em vez de apenas um nome de arquivo — o que permite localizá-los em qualquer local fixo no sistema de arquivos. (É importante lembrar que para o cliente, o WSDL incluindo as extensões personalizadas WSIT deve usar o nome fixo wsit-client.xml e deve estar em um diretório-raiz do caminho de classe, ou no diretório filho META-INF de um diretório-raiz no caminho de classe.)

Uso no lado do servidor

As extensões personalizadas WSIT do lado do servidor adicionadas ao WSDL são mostradas na Listagem 8 (novamente em negrito). Nesse caso, o keypass do atributo <wsss:KeyStore> fornece um nome de classe ao invés de um valor real de senha (como no exemplo do lado do cliente na Listagem 7). Quando essa abordagem é utilizada, a classe referenciada deve implementar a interface javax.security.auth.callback.CallbackHandler e será chamada pelo código WSIT quando ele precisar acessar a senha para a chave secreta. Também é possível utilizar esta mesma técnica de especificar um nome de classe ao invés de um valor de senha para valores de storepass.

Listagem 8. Política de assinatura e criptografia com extensões WSIT do lado do servidor
<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>

A Listagem 9 mostra a implementação da interface CallbackHandler usada para este exemplo:

Listagem 9. Código de retorno de chamada de senha de armazém de chave do servidor
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");
            }
        }
    }
}

Construindo e executando o código da amostra

O exemplo de assinatura e criptografia usa as mesmas etapas de construção que o exemplo do Token do nome de usuário, exceto que é necessário alterar o arquivo build.properties para usar variant-name=signencr (ao invés do valor nome de usuário para o exemplo do token do nome de usuário).

Problema de interoperabilidade

Se tentar usar o cliente Axis2/Rampart com o servidor Metro/WSIT (ou vice-versa) pode haver um problema quando o cliente tentar adicionar um livro com um Número Padrão Internacional de Livro (ISBN) duplicado. Nesse caso, o serviço retorna uma Falhar, e não a mensagem SOAP de resposta normal. Os releases Axis2/Rampart 1.5.x realizam corretamente o tratamento usual de assinatura e criptografia exigido pelo WSDL nesse caso, mas o Metro/WSIT 1.5 não, resultando em uma falha no cliente. Este é um erro no código WSIT, que deve ser corrigido para o próximo release do Metro.

Se executar o teste com uma versão anterior do Axis2/Rampart, provavelmente não haverá problemas — pois o Rampart teve o mesmo erro até o release Rampart 1.5.


Próxima parada no Metro

O suporte WSIT do Metro para WS-SecurityPolicy permite a configuração direta de parâmetros, como nomes de usuários e senhas (inclusive senhas de armazém de chave e de chaves particulares) e o uso de chamadas de retorno para obter esses valores conforme necessário. Ele também permite escolher entre a manipulação da autorização do contêiner do servlet e sua própria chamada de retorno para verificar as combinações de nome de usuário e senha no servidor. Essa flexibilidade permite que o Metro atenda às necessidades de muitos tipos de aplicativos facilmente. Também é conveniente que o Metro venha com suporte WSIT/XWSS WS-Security como componentes integrados, ao invés de um componente separado (com seus próprios ciclos de liberação e geralmente incompatibilidades entre versões diferentes do principal componente) como com o Axis2 e o Rampart.

No lado negativo, as informações são escassas sobre o uso independente do Metro/WSIT e configuração dele diretamente (como oposta ao uso dele em combinação com o NetBeans IDE e servidor de aplicativos Glassfish). Muitas das opções necessárias são documentadas somente em postagens de blog ou trocas de e-mails (consultar Recursos).

A próxima parte dos Java Web services continua com o foco no Metro, dessa vez voltado ao desempenho. Veja como o desempenho do Metro se compara ao Axis2, para trocas simples de mensagens e com o WS-Security em uso.


Download

DescriçãoNomeTamanho
Source code for this articlej-jws10.zip38KB

Recursos

Aprender

Obter produtos e tecnologias

Discutir

Comentários

developerWorks: Conecte-se

Los campos obligatorios están marcados con un asterisco (*).


Precisa de um ID IBM?
Esqueceu seu ID IBM?


Esqueceu sua senha?
Alterar sua senha

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


A primeira vez que você entrar no developerWorks, um perfil é criado para você. Informações no seu perfil (seu nome, país / região, e nome da empresa) é apresentado ao público e vai acompanhar qualquer conteúdo que você postar, a menos que você opte por esconder o nome da empresa. Você pode atualizar sua conta IBM a qualquer momento.

Todas as informações enviadas são seguras.

Elija su nombre para mostrar



Ao se conectar ao developerWorks pela primeira vez, é criado um perfil para você e é necessário selecionar um nome de exibição. O nome de exibição acompanhará o conteúdo que você postar no developerWorks.

Escolha um nome de exibição de 3 - 31 caracteres. Seu nome de exibição deve ser exclusivo na comunidade do developerWorks e não deve ser o seu endereço de email por motivo de privacidade.

Los campos obligatorios están marcados con un asterisco (*).

(Escolha um nome de exibição de 3 - 31 caracteres.)

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


Todas as informações enviadas são seguras.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=80
Zone=Tecnologia Java
ArticleID=457145
ArticleTitle=Java Web Services: WS-Security com Metro
publish-date=12162009