2009-03-30 19 views
9

Sto provando a sviluppare un client di servizi Web Java autonomo con JAX-WS (Metro) che utilizza WS-Security con autenticazione token del nome utente (Digest di password, nonces e timestamp) e la verifica del timestamp insieme a WS-Addressing su SSL.JAX-WS Servizio Web di consumo con WS-Security e WS-Addressing

Il WSDL con cui devo lavorare non definisce alcuna informazione sulla politica di sicurezza. Non sono stato in grado di capire esattamente come aggiungere queste informazioni di intestazione (il modo corretto per farlo) quando il WSDL non contiene queste informazioni. La maggior parte degli esempi che ho trovato usando Metro ruotano attorno all'utilizzo di Netbeans per generare automaticamente questo dal WSDL che non mi aiuta affatto. Ho esaminato WSIT, XWSS, ecc. Senza molta chiarezza o direzione. JBoss WS Metro sembrava promettente, ma non molto fortunato ancora.

Chiunque ha esperienza in questo o ha suggerimenti su come eseguire questa attività? Sarebbe utile anche indicarmi la giusta direzione. Non sono limitato a una tecnologia specifica diversa da quella che deve essere basata su Java.

risposta

8

Ho finito per capire questo problema ma sono andato in un'altra direzione per farlo. La mia soluzione era usare CXF 2.1 e la sua implementazione JAX-WS, combinando la potenza di CXF con l'infrastruttura Spring esistente che avevo già installato. All'inizio ero scettico a causa dei numerosi barattoli richiesti da CXF, ma alla fine ha fornito la soluzione migliore e più semplice.

Adattando un esempio dallo CXF website for client configuration, ho utilizzato lo spazio dei nomi JXWS CXF personalizzato entro la primavera e ho utilizzato un intercettatore aggiuntivo per l'autenticazione token del nome utente (digitazione password, nonces e timestamp) e la verifica dell'indicatore di data e ora. L'unico altro passo per fare questo lavoro è stato creare il mio gestore Callback password che viene eseguito per ogni richiesta SOAP in uscita.

Per la configurazione SSL, ho nuovamente passato a CXF and its SSL support via conduits, anche se non potrei mai fare funzionare SSL con un nome http: conduit specifico, ho dovuto usare quello generale che non è raccomandato per gli ambienti di produzione.

Di seguito è riportato un esempio del mio file di configurazione.

file di Primavera di configurazione

<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 client password 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

Se le informazioni non sono nel WSDL, sei sicuro che sia nel servizio descritto dal WSDL? Il WSDL ha lo scopo di fornire tutte le informazioni necessarie per descrivere il servizio, comprese le politiche di sicurezza necessarie per utilizzare il servizio.

Da quale piattaforma è nato il WSDL? È possibile che WSDL non sia la descrizione completa? Ad esempio, potrebbe essere un WSDL che è includere d in un altro WSDL che fa fornire le informazioni di sicurezza.

+0

Mi permetta di chiarire, non è nel WSDL a cui mi è stato concesso l'accesso. La documentazione che ho ha dei buchi (nessuna menzione di timestamp), quindi l'esaltazione delle informazioni WSDL non mi sorprenderebbe. –

+0

Bene, avrai bisogno del vero WSDL per chiamare il vero servizio.Questo è tutto quello che c'è da fare. –

+0

Quindi non c'è alcuna possibilità che il WSDL lo lasci fuori ma è implementato sul server? Il server è Java (probabilmente JBoss o Websphere) che utilizza una versione di Axis. –

Problemi correlati