lunes, 15 de junio de 2015

Como agregar un wsee-security y basic-authentication aun webservice con JAX-WS

Después de investigar un problema relacionado con la duplicidad de encabezados en los Handlers al intentar manipular el mensaje SOAP que enviamos mediante un WebService utilizando la libreria JAX-WS, he encontrado al fin una solución que les aliviará los dolores de cabeza con estos errores. 

Seguramente te habrás encontrado la duplicidad de targetnamepsace:

  • S="http://schemas.xmlsoap.org/soap/envelope/" 
  • SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/". 


El posible origen del problema es que estas usando JAX-WS para generar el servicio web (bajo JAX-B) y posteriormente usas SoapHandler (bajo SAAJ) para manipular el mensaje SOAP.

¿Cómo lo sé? La respuesta es que el targetnamepsace S lo genera JAXB y el targetnamepsace SOAP-ENV lo genera SAAJ.

Al usar el SOAPHandler posiblemente estés obteniendo el mensaje de esta forma:
    SOAPMessage msg = SOAPMessageContext.getMessage();
Al obtener el mensaje usando  SOAPMessageContext.getMessage() automaticamente SAAJ intenta crear una cabecera por lo que agrega un header vacío. y en su defecto declara el nuevo targetnamespace SOAP-ENV.

Para resolver el problema, diseñe una clase Helper que te facilitará el trabajo de agregar en JAX-WS la cabecera sin manipular el mensaje. Además verás otras utilidades interesantes que se pueden incluir en el helper.

clase JAXWSHelper
package com.hek.ws.handler;

import java.util.Map;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.ws.BindingProvider;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import com.inetpsa.ped.core.PEDTechnicalException;
import com.sun.xml.ws.api.message.Header;
import com.sun.xml.ws.developer.WSBindingProvider;
import com.sun.xml.ws.message.DOMHeader;

/**
 * Helper class only to use with JAX-WS and useful to obtain a reference to the binding provider and to append different WebServices operations:
 * <p>
 * <ul>
 * <li>basic authentication</li>
 * <li>security headers on paylaod</li>
 * <li>add published url service</li>
 * <li>among others</li>
 * </ul>
 * </p>
 * You can downcast to WSBindingProvider which defines a few more methods provided only by the JAX-WS RI.
 * <p />
 * If you dont use JAX-WS, not use this helper class. You can use javax.xml.ws.handler.soap.SoapHandler to adding security headers
 * 
 * @author E460165
 * @param <S> the BindingProvider generic type
 */
public class JAXWSHelper<S extends BindingProvider> {

    /** The Constant WS_SECEXT. */
    public final static String WS_SECEXT = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";

    /** The Constant WS_USER_TOKEN_PROFILE. */
    public final static String WS_USER_TOKEN_PROFILE = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText";

    /** The provider. */
    private S provider;

    /**
     * Instantiates a new wss ws security helper.
     * 
     * @param provider the provider
     * @param username the username
     * @param password the password
     */
    public JAXWSHelper(S provider) {
        this.provider = provider;
    }

    /**
     * Adds the http basic authorization.
     */
    public void addHttpBasicAuthorization(String username, String password) {
        Map<String, Object> requestContext = provider.getRequestContext();
        requestContext.put(BindingProvider.USERNAME_PROPERTY, username);
        requestContext.put(BindingProvider.PASSWORD_PROPERTY, password);
    }

    /**
     * Adds the wsse security relates to header and asigned username and password.
     */
    public void addWsseSecurityHeader(String username, String password) {
        try {
            Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
            Element root = document.createElementNS(WS_SECEXT, "wsse:Security");
            document.appendChild(root);

            Element usernameTokenEl = document.createElementNS(WS_SECEXT, "wsse:UsernameToken");
            root.appendChild(usernameTokenEl);

            Element usernameEl = document.createElementNS(WS_SECEXT, "wsse:Username");
            usernameTokenEl.appendChild(usernameEl);
            usernameEl.setTextContent(username);

            Element passwordEl = document.createElementNS(WS_SECEXT, "wsse:Password");
            usernameTokenEl.appendChild(passwordEl);
            passwordEl.setTextContent(password);
            passwordEl.setAttribute("Type", WS_USER_TOKEN_PROFILE);

            Header securityHeader = new DOMHeader<Element>(document.getDocumentElement());

            if (provider instanceof WSBindingProvider) {
                ((WSBindingProvider) provider).setOutboundHeaders(securityHeader);
            }
        } catch (ParserConfigurationException e) {
            throw new PEDTechnicalException("Error adding ws-security header to the webservice", e);
        }
    }

    /**
     * Adds the http endpoint.
     * 
     * @param url the url
     */
    public void addHttpEndpoint(String url) {
        Map<String, Object> requestContext = provider.getRequestContext();
        requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, url);
    }
}
Ejemplo de como utilizar la clase JAXWSHelper
    /** The url service bps. */
    private String URL_SERVICE_BPS;

    /** The wsdl resource. */
    private String WSDL_RESOURCE;

    /** The username. */
    private String USERNAME;

    /** The password. */
    private String PASSWORD;

    private AccesBPSPortType createClientBps() {
        URL wsdlURL = this.getClass().getClassLoader().getResource(WSDL_RESOURCE);

        // call webservice
        WebServiceClient webServiceClient = AccesBPS.class.getAnnotation(WebServiceClient.class);
        AccesBPS accesBps = new AccesBPS(wsdlURL, new QName(webServiceClient.targetNamespace(), webServiceClient.name()));

        AccesBPSPortType client = accesBps.getPort(AccesBPSPortType.class);

        JAXWSHelper jaxWSHelper = new JAXWSHelper(((BindingProvider) client));

        // adding http basic authorization
        jaxWSHelper.addHttpBasicAuthorization(USERNAME, PASSWORD);
        // adding wsse security headers
        jaxWSHelper.addWsseSecurityHeader(USERNAME, PASSWORD);
        // adding httpEndpoint
        jaxWSHelper.addHttpEndpoint(URL_SERVICE_BPS);

        return client;

    }

1 comentario:

  1. Saludos me parece u muy buen articulo, estoy en una situación similar,,, no se tal vez me podrías hechar una mano,, no se como contactarte me ayudarías muchísimo mi correo electrónico es jhonnyjgc@yahoo.com saludos espero respondas mi comentario

    ResponderEliminar