UsernameToken en Jax-ws (2/2) – Cliente

El objetivo de este artículo es mostrar como implementar crear un cliente Jax-ws que consuma servicios securizados con   UsernameToken según la especificación Web Services Security UsernameToken Profile 1.0.

El artículo consiera que el lector ya tiene experiencia en Jax-ws y se centra sólo en la configuración del mecanismo de seguridad  consumir servicios que requieran la identificación del solicitante con usuario y password haciendo uso de Jax-ws.

Dependencias requeridas

Aunque las dependencias necesarias están incluidas en algunos servidores de aplicaciones, si lo deseamos podemos o bien bajar la implementación de referencia directamente desde su web:

https://jax-ws-commons.dev.java.net y https://jax-ws.dev.java.net

O definir la dependencia en caso de que el proyecto sea Maven.

[code]
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.1.12</version>
</dependency>

<dependency>
<groupId>com.sun.xml.wss</groupId>
<artifactId>xws-security</artifactId>
<version>3.0</version>
</dependency>

<dependency>
<groupId>com.sun.org.apache.xml.security</groupId>
<artifactId>xmlsec</artifactId>
<version>2.0</version>
</dependency>

<dependency>
<groupId>javax.xml</groupId>
<artifactId>jaxp-api</artifactId>
<version>1.4.2</version>
</dependency>

<dependency>
<groupId>activesoap</groupId>
<artifactId>xercesImpl</artifactId>
<version>1.5</version>
</dependency>

<dependency>
<groupId>activesoap</groupId>
<artifactId>jaxb-xalan</artifactId>
<version>1.5</version>
</dependency>
[/code]

Configuaramos el acceso al Servicio

[code lang=»java»]

URL url = new URL("http://rutaAplicacionDeEjemplo/IE3SEnvironmentalMasterData");
QName qname = new QName("e3swsdl-master", "IE3SEnvironmentalMasterData");
IE3SEnvironmentalMasterDataService locator = new IE3SEnvironmentalMasterDataService(url, qname);
IE3SEnvironmentalMasterData clienteProxy = locator.getPort(IE3SEnvironmentalMasterData.class);

//Añadimos el manejador
final List<Handler> chain = new ArrayList<Handler>();
chain.add(new SecurityHandler());
((BindingProvider) clienteProxy).getBinding().setHandlerChain(chain);

//Accedemos al servicio
clienteProxy.addTemporaryProducerCenterData(null, null, null, null, holder);

[/code]

Creamos el manejador (SecurityHandler.java)

[code lang=»java»]
import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.Set;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
import com.sun.xml.wss.ProcessingContext;
import com.sun.xml.wss.XWSSProcessor;
import com.sun.xml.wss.XWSSProcessorFactory;
import com.sun.xml.wss.XWSSecurityException;

public class SecurityHandler implements SOAPHandler<SOAPMessageContext> {

XWSSProcessor cprocessor = null;

public SecurityHandler() {
//Leemos el archivo de configuración del servidor
final InputStream input = this.getClass().getResourceAsStream("/user-pass-authenticate-client.xml");

try {
// inicializamos el XWSSProcessor
final XWSSProcessorFactory factory = XWSSProcessorFactory.newInstance();
cprocessor = factory.createProcessorForSecurityConfiguration(input, new SecurityEnvironmentHandler());
input.close();

} catch (final XWSSecurityException e) {
//TODO tratar la excepción
throw new RuntimeException(e);
} catch (final IOException e) {
//TODO tratar la excepción
}

}

public Set<QName> getHeaders() {
final QName securityHeader = new QName("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", "Security", "wsse");
final HashSet<QName> headers = new HashSet<QName>();
headers.add(securityHeader);
return headers;
}

public boolean handleFault(final SOAPMessageContext messageContext) {
return true;
}

public boolean handleMessage(final SOAPMessageContext messageContext) {
return secureClient(messageContext);

}

public void close(final MessageContext messageContext) {
}

private boolean secureClient(final SOAPMessageContext messageContext) {
final Boolean outMessageIndicator = (Boolean) messageContext.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
final SOAPMessage message = messageContext.getMessage();
boolean ok = false;
if (outMessageIndicator.booleanValue()) {
ProcessingContext context;
try {
context = cprocessor.createProcessingContext(message);
context.setSOAPMessage(message);
final SOAPMessage secureMsg = cprocessor.secureOutboundMessage(context);
secureMsg.writeTo(System.out);
messageContext.setMessage(secureMsg);
ok = true;
} catch (final Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
} else {
ok = false;
}
return ok;
}
}
[/code]

Creamos la clase que maneja la seguridad (SecurityEnvironmentHandler.java)

[code lang=»java»]
import java.io.IOException;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import com.sun.xml.wss.impl.callback.PasswordCallback;
import com.sun.xml.wss.impl.callback.UsernameCallback;

/**
* Handle the WSS user/pass security.
*/
public class SecurityEnvironmentHandler implements CallbackHandler {

public void handle(final Callback[] callbacks) throws IOException, UnsupportedCallbackException {
for (final Callback callback : callbacks) {
if (callback instanceof UsernameCallback) {

final UsernameCallback cb = (UsernameCallback) callback;
final String userName = "USUARIO";
if (userName == null) {
throw new IOException("La configuaricion del parametro ‘" + "USARIO" + "’ es requerida");
}
cb.setUsername(userName);

} else if (callback instanceof PasswordCallback) {
final String pass = "CLAVE";
if (pass == null) {
throw new IOException("La configuaricion del parametro ‘" + "CLAVE" + "’ es requerida");
}

final PasswordCallback cb = (PasswordCallback) callback;
cb.setPassword(pass);
}
}
}
}
[/code]

Configuramos el manejador (user-pass-authenticate-client.xml)

[code]
<xwss:SecurityConfiguration xmlns:xwss="http://java.sun.com/xml/ns/xwss/config" dumpMessages="true" >
<xwss:UsernameToken digestPassword="false"/>
</xwss:SecurityConfiguration>
[/code]

Nuestra petición de acceso debe contener el usuario y contraseña de acceso en la cabecera de la petición http siendo similar a la siguiente

[code]
<pre><code><?xml version="1.0" encoding="UTF-8"?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" S:mustUnderstand="1">
<wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="XWSSGID-1255949753848350309586">
<strong><wsse:Username>usuario</wsse:Username></strong>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">****</wsse:Password>
<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">9w+YwOatF9l1/otioQ75d5Yr</wsse:Nonce>
<wsu:Created>2009-10-19T10:55:54.418Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</S:Header>
<S:Body>
</S:Body>
</S:Envelope>
</code></pre>
[/code]