UsernameToken en Jax-ws (2/2) – Cliente
25 jun 2010
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.
<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>
Configuaramos el acceso al Servicio
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);
Creamos el manejador (SecurityHandler.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;
}
}
Creamos la clase que maneja la seguridad (SecurityEnvironmentHandler.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);
}
}
}
}
Configuramos el manejador (user-pass-authenticate-client.xml)
<xwss:SecurityConfiguration xmlns:xwss="http://java.sun.com/xml/ns/xwss/config" dumpMessages="true" > <xwss:UsernameToken digestPassword="false"/> </xwss:SecurityConfiguration>
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
<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>