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);
}
}
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. –
Bueno, vas a necesitar el WSDL real para llamar al servicio real.Eso es todo lo que hay para eso. –
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. –