Pergunta

Eu estou tentando desenvolver um cliente independente do serviço web Java com JAX-WS (Metro) que usa WS-Security com autenticação de token de usuário (senha digest, nonces e timestamp) e verificação timestamp junto com WS-Addressing sobre SSL.

O WSDL Eu tenho que trabalhar com não define qualquer informação política de segurança. Eu fui incapaz de descobrir exatamente como adicionar essas informações de cabeçalho (a maneira correta de fazê-lo) quando o WSDL não contém essa informação. A maioria dos exemplos que eu encontrei usando Metro giram em torno usando o NetBeans para gerar automaticamente isso a partir do WSDL que não me ajuda em nada. Eu olhei em WSIT, XWSS, etc., sem muita clareza ou direção. JBoss WS Metro olhou prometendo não muita sorte ainda lá.

Alguém tem experiência fazendo isso ou tem sugestões sobre como realizar essa tarefa? Mesmo me apontar na direção certa seria útil. Não estou restrito a uma tecnologia específica que não deve ser baseado em Java.

Foi útil?

Solução

Eu fiz acabam por descobrir esta questão, mas eu fui em outra direção para fazê-lo. Minha solução foi usar CXF 2.1 e sua implementação JAX-WS, combinando o poder de CXF com a infra-estrutura existente da Primavera eu já tinha no lugar. Eu estava cético no início por causa dos inúmeros frascos exigidos pelo CXF, mas, no fim, desde que o melhor e mais simples solução.

Adaptar um exemplo de CXF site para configuração do cliente o, Eu usei o costume CXF JAXWS namespace dentro de primavera e usou um Interceptor fora para autenticação de token de usuário (senha digest, nonces e timestamp) e verificação timestamp. A única outra etapa para fazer este trabalho foi criar a minha própria senha Callback manipulador que é executado para cada solicitação SOAP de saída.

Para a configuração SSL, mais uma vez virou-se para CXF e seu suporte SSL via condutas , embora eu nunca poderia fazer o trabalho de SSL com um específico http:. nome do canal, eu tive que usar o propósito geral que não é recomendado para ambientes de produção

A seguir é um exemplo de meu arquivo de configuração.

arquivo de configuração da Primavera

<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:sec="http://cxf.apache.org/configuration/security"
    xmlns:http="http://cxf.apache.org/transports/http/configuration"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:cxf="http://cxf.apache.org/core"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
    http://cxf.apache.org/configuration/security http://cxf.apache.org/schemas/configuration/security.xsd
    http://cxf.apache.org/transports/http/configuration http://cxf.apache.org/schemas/configuration/http-conf.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
    http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd
    http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd">

    <context:property-placeholder location="meta/my.properties" />
    <context:component-scan base-package="com.foo" />

    <import resource="remoting.xml" />
    <jaxws:client id="myWebService" address="${my.endpointAddress}"
                  serviceClass="com.foo.my.ServicePortType">

<!-- Testing only, adds logging of entire message in and out -->
<jaxws:outInterceptors>
    <ref bean="TimestampUsernameToken_Request" />
    <ref bean="logOutbound" />
</jaxws:outInterceptors>
<jaxws:inInterceptors>
        <ref bean="logInbound" />
    </jaxws:inInterceptors>
    <jaxws:inFaultInterceptors>
        <ref bean="logOutbound" />
    </jaxws:inFaultInterceptors>

<!-- Production settings -->
<!--
    <jaxws:outInterceptors> <ref bean="TimestampUsernameToken_Request" />
    </jaxws:outInterceptors>
    -->
</jaxws:client >



<!--
    CXF Interceptors for Inbound and Outbound messages
    Used for logging and adding Username token / Timestamp Security Header to SOAP message
-->
<bean id="logInbound" class="org.apache.cxf.interceptor.LoggingInInterceptor" />
<bean id="logOutbound" class="org.apache.cxf.interceptor.LoggingOutInterceptor" />

<bean id="TimestampUsernameToken_Request" class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor">
    <constructor-arg>
        <map>
            <entry key="action" value="UsernameToken Timestamp" />
            <entry key="user" value="${my.group}.${my.userId}" />
            <entry key="passwordType" value="PasswordDigest" />
            <entry key="passwordCallbackClass" value="com.foo.my.ClientPasswordHandler" />
        </map>
    </constructor-arg>
</bean>

<!--
    http:conduit namespace is used to configure SSL using keystores, etc
    *.http-conduit works but CXF says its only supposed to be for temporary use (not production),
    well until the correct way works, we're going to use it.
-->
<http:conduit name="*.http-conduit">
    <http:tlsClientParameters   
                  secureSocketProtocol="SSL">
                  <!--
          <sec:trustManagers>
        <sec:keyStore type="JKS"
                         password="${my.truststore.password}"
                         file="${my.truststore.file}" />
                  </sec:trustManagers>
                  -->
                  <sec:keyManagers keyPassword="${my.keystore.password}">
                    <sec:keyStore type="JKS"
                         password="${my.keystore.password}"
                         file="${my.keystore.file}" />
                  </sec:keyManagers>

                  <!-- Cipher suites filters specify the cipher suite to allow/disallow in SSL communcation  -->
                  <sec:cipherSuitesFilter>
                    <sec:include>.*_WITH_3DES_.*</sec:include>
                    <sec:include>.*_EXPORT_.*</sec:include>
                    <sec:include>.*_EXPORT1024_.*</sec:include
                    <sec:include>.*_WITH_DES_.*</sec:include
                    <sec:exclude>.*_WITH_NULL_.*</sec:exclude
                    <sec:exclude>.*_DH_anon_.*</sec:exclude>
                  </sec:cipherSuitesFilter>
    </http:tlsClientParameters>
</http:conduit>
</beans>

Java Cliente senha Handler :

import java.io.IOException;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;

import org.apache.log4j.Logger;
import org.apache.ws.security.WSPasswordCallback;


/**
 * <p>
 * Provides a callback handler for use processing outbound/inbound SOAP messages.
 * ClientPasswordHandler sets the password used in the WS-Security UsernameToken 
 * SOAP header.
 * 
 * </p>
 * 
 * Created: Apr 1, 2009
 * @author Jared Knipp
 * 
 */
public final class ClientPasswordHandler implements CallbackHandler {
    protected static Logger log = Logger.getLogger(ClientPasswordHandler.class);

    private static final PropertyManager PROPS = PropertyManager.getInstance();
    private static String PASSWORD = PROPS.getPassword();
    private static boolean IS_PASSWORD_CLEAR = PROPS.getIsClearPassword();

    /**
     * Client password handler call back.  This method is used to provide
     * additional outbound (or could be inbound also) message processing.
     * 
     * Here the method sets the password used in the UsernameToken SOAP security header
     * element in the SOAP header of the outbound message.  For our purposes the clear 
     * text password is SHA1 hashed first before it is hashed again along with the nonce and 
     * current timestamp in the security header.
     */
    public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
        if(log.isDebugEnabled()) { log.debug("Setting password for UsernameToken"); }
        WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];


        // Check to see if the password is already Hashed via SHA1, if not then hash it first
        if(IS_PASSWORD_CLEAR) {
            synchronized(this) {
                PASSWORD = PasswordDigestUtil.doPasswordDigest(PASSWORD);
                IS_PASSWORD_CLEAR = false;
                PROPS.setIsClearPassword(IS_PASSWORD_CLEAR);
                PROPS.setPassword(PASSWORD);
                PROPS.saveProperties();
            }
        }

        pc.setPassword(PASSWORD);
    }
}

Outras dicas

Se a informação não está no WSDL, você tem certeza que é no serviço descrito pelo WSDL? O WSDL se destina a fornecer todas as informações necessárias para descrever o serviço, incluindo as políticas de segurança necessárias para usar o serviço.

O que plataforma que o WSDL vem? É possível que o WSDL não é a descrição completa? Por exemplo, pode ser um WSDL que é incluir d em outra WSDL que faz fornecer as informações de segurança.

Há um post aqui explicando como configurar um cliente e um servidor no CXF com WS-Security: JAX-WS web Services com a Primavera e CXF

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top