2009-03-30 25 views
9

Estoy tratando de desarrollar un cliente independiente de servicios web Java con JAX-WS (Metro) que use WS-Security con autenticación Token de nombre de usuario (Password digest, nonces y marca de tiempo) y la verificación de la marca de tiempo junto con WS-Addressing over SSL.JAX-WS Consumo de servicio web con WS-Security y WS-Addressing

El WSDL con el que tengo que trabajar no define ninguna información de política de seguridad. No he podido averiguar exactamente cómo agregar esta información de encabezado (la forma correcta de hacerlo) cuando el WSDL no contiene esta información. La mayoría de los ejemplos que he encontrado usando Metro giran en torno al uso de Netbeans para generar automáticamente esto a partir del WSDL que no me ayuda en absoluto. He investigado WSIT, XWSS, etc. sin mucha claridad ni dirección. JBoss WS Metro parecía prometedor y no tenía mucha suerte allí tampoco.

¿Alguien tiene experiencia haciendo esto o tiene sugerencias sobre cómo lograr esta tarea? Incluso señalarme en la dirección correcta sería útil. No estoy restringido a una tecnología específica distinta de la que debe estar basada en Java.

Respuesta

8

Terminé pensando en este problema pero fui en otra dirección para hacerlo. Mi solución fue usar CXF 2.1 y su implementación JAX-WS, combinando la potencia de CXF con la infraestructura de Spring existente que ya tenía instalada. Al principio era escéptico debido a los numerosos frascos requeridos por CXF, pero al final me proporcionó la mejor y más simple solución.

Adaptación de un ejemplo de la CXF website for client configuration, he utilizado la costumbre CXF JAXWS espacio de nombres dentro de la primavera y se utiliza un interceptor de salida de nombre de usuario Token de autenticación (contraseña Digest, valores de uso único y marca de tiempo) y la verificación de marca de tiempo. El único otro paso para hacer que esto funcione fue crear mi propio controlador de devolución de llamada por contraseña que se ejecuta para cada solicitud SOAP de salida.

Para la configuración de SSL, volví a usar CXF and its SSL support via conduits, aunque nunca pude hacer que SSL funcionara con un nombre http: conduit específico, tuve que usar el de propósito general que no se recomienda para entornos de producción.

A continuación se muestra un ejemplo de mi archivo de configuración.

archivo de configuración 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> 

Cliente Java contraseña 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); 
    } 
} 
0

Si la información no está en el WSDL, ¿está seguro de que está en el servicio descrito por el WSDL? El WSDL está destinado a proporcionar toda la información necesaria para describir el servicio, incluidas las políticas de seguridad necesarias para utilizar el servicio.

¿De qué plataforma proviene WSDL? ¿Es posible que el WSDL no sea la descripción completa? Por ejemplo, podría ser un WSDL que es incluir d en otro WSDL que hace proporciona la información de seguridad.

+0

Déjenme aclarar, no está en el WSDL al que se me ha dado acceso. La documentación que tengo tiene agujeros (sin mención de marca de tiempo) por lo que la exclusión de la información WSDL no me sorprendería. –

+0

Bueno, vas a necesitar el WSDL real para llamar al servicio real.Eso es todo lo que hay para eso. –

+0

Entonces, ¿no hay posibilidad de que el WSDL lo omita pero está implementado en el servidor? El servidor es Java (probablemente JBoss o Websphere) usando alguna versión de Axis. –

Cuestiones relacionadas