Pages

January 29, 2011

How to add a SOAP header using jax-ws

In this article I will add a security header to the soap request. So I will create a class implementing the interface SOAPHandler and I will override the method handleMessage:



@Override
    public boolean handleMessage(SOAPMessageContext context) {
        String prefixUri = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-";
        String uri = prefixUri + "wssecurity-secext-1.0.xsd";
        String uta = prefixUri + "wssecurity-utility-1.0.xsd";
        String ta = prefixUri + "username-token-profile-1.0#PasswordText";
        Boolean outboundProperty =
            (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
        if (outboundProperty.booleanValue()) {
            try {
                SOAPEnvelope envelope = context.getMessage()
                        .getSOAPPart().getEnvelope();
                SOAPFactory factory = SOAPFactory.newInstance();
                String prefix = "wsse";
                SOAPElement securityElem =
                        factory.createElement("Security",prefix,uri);
                SOAPElement tokenElem =
                        factory.createElement("UsernameToken",prefix,uri);
                tokenElem.addAttribute(QName.valueOf("wsu:Id"),"UsernameToken-2");
                tokenElem.addAttribute(QName.valueOf("xmlns:wsu"), uta);
                SOAPElement userElem =
                        factory.createElement("Username",prefix,uri);
                userElem.addTextNode("myUser");
                SOAPElement pwdElem =
                        factory.createElement("Password",prefix,uri);
                pwdElem.addTextNode("myPwd");
                pwdElem.addAttribute(QName.valueOf("Type"), ta);
                tokenElem.addChildElement(userElem);
                tokenElem.addChildElement(pwdElem);
                securityElem.addChildElement(tokenElem);
                SOAPHeader header = envelope.addHeader();
                header.addChildElement(securityElem);

            } catch (Exception e) {
                System.out.println("Exception in handler: " + e);
            }
        }
        
        return true;
    }


Now, I will add a simple call to this class in my implementation client:


service.setHandlerResolver(new HandlerResolver() {
            @Override
    public List<Handler> getHandlerChain(PortInfo portInfo) {
        List<Handler> handlerList = new ArrayList<Handler>();
        handlerList.add(new MySoapHandler());
        return handlerList;
    }
});




6 comments:

  1. Thanks Moez,

    Your solution is generic, if you want to deal only with security header you can use ws*-security like :

    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.ws.security.WSPasswordCallback;

    public class ClientPasswordCallback implements CallbackHandler {

    public void handle(Callback[] callbacks) throws IOException,
    UnsupportedCallbackException {
    WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];

    if ("user".equals(pc.getIdentifier())) {
    pc.setPassword("password");

    } // else {...} - can add more users, access DB, etc.
    }
    }

    and



    Map outProps = new HashMap();
    Client client = org.apache.cxf.frontend.ClientProxy.getClient(port);
    Endpoint cxfEndpoint = client.getEndpoint();

    // Manual WSS4JOutInterceptor interceptor process
    outProps.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
    outProps.put(WSHandlerConstants.USER, "user");
    outProps.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
    outProps.put(WSHandlerConstants.PW_CALLBACK_CLASS,
    ClientPasswordCallback.class.getName());
    outProps.put(WSHandlerConstants.TIMESTAMP, new Date());
    outProps.put(WSHandlerConstants.MUST_UNDERSTAND, "0");


    WSS4JOutInterceptor wssOut = new WSS4JOutInterceptor(outProps);

    List> outInterceptors = cxfEndpoint.getOutInterceptors();
    outInterceptors.add(wssOut);

    ReplyDelete
  2. This blog is related shopping. New ideas, quality designs, sensible colors, and no-nonsense prices. I want to like the bag, but something is keeping me from liking it. Unfortunate because I own so many of his bags. This is really nice blog…


    Firmagaver

    ReplyDelete
  3. Awesome man...works like a charm...

    ReplyDelete
  4. When I tried this, I got the following exception: com.sun.xml.ws.message.saaj.SAAJHeader cannot be cast to com.sun.xml.ws.security.opt.impl.outgoing.SecurityHeader
    I'm using a standalone java client. I tried JDK 6 and JDK 7. Any ideas? Thanks!

    ReplyDelete
  5. which server you use? It's a problem of lib versions

    ReplyDelete
    Replies
    1. I've got it working using just JDK 6 now. It turns out that if I have the various webservices-* jars from Metro in my classpath, I get that error. When I remove them, the code works fine. That's great, except that my application needs to run under Tomcat 6 and both host web services and be a client for web service calls. I can't host without the webservices-* jars. I can't be a client with the webservices-* jars. Very, very frustrating.

      Delete