Sunday, February 3, 2013

JAX-WS Password Type PasswordText


I've got a simple command line Java JAX-WS app to test a SOAP request, but the server is expecting the Password Type to be PasswordText and I'm stumped on how to set this...
The code looks like so:
@WebServiceRef
private static final HelloService helloService = new HelloService(url, new QName(
        URL, "HelloService"));

public static void main(final String... args) {

    try {
        final HelloPort helloPort = helloService.getHelloPort();
        final BindingProvider hB = ((BindingProvider) helloPort);
        hB.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
                                   END_POINT_ADDRESS);
        hB.getRequestContext().put(BindingProvider.USERNAME_PROPERTY,
                                   USERNAME);
        hB.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY,
                                   PASSWORD);
        ...
I've tested the request using SOAP-UI so I know it's working. Any help on setting the password type would be appreciated.

That will set the username and password for Basic HTTP authentication. If you've tested it in SoapUI, I'm guessing the 'PasswordText' value you speak of is the 'WSS-Password Type' in the request details pane. That sets WSS security, not HTTP security.
With JAX-WS in Java6 you need to attach a SOAPHandler to inject the WSS-Usertoken into the SOAP Header. There are plenty of bits and bobs about this round the net, but I couldn't find one single link to post, so here's some code instead to get you going...
To add a handler you need something like:
final Binding binding = ((BindingProvider) servicePort).getBinding();
List<Handler> handlerList = binding.getHandlerChain();
if (handlerList == null)
    handlerList = new ArrayList<Handler>();

handlerList.add(new SecurityHandler());
binding.setHandlerChain(handlerList); // <- important="" span="">
Then the SecurityHandler class will do the deed. Handlers are general things and get called for both successful messages and for faults, but perhaps more importantly they get called in both message directions - for the outgoing request and then again for the incoming response. You only want to handle outgoing messages. So you'll need something like:
public final class SecurityHandler implements SOAPHandler<SOAPMessageContext> {

    ...

    @Override
    public boolean handleMessage(final SOAPMessageContext msgCtx) {

        // Indicator telling us which direction this message is going in
        final Boolean outInd = (Boolean) msgCtx.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);

        // Handler must only add security headers to outbound messages
        if (outInd.booleanValue()) {
            try {
                // Get the SOAP Envelope
                final SOAPEnvelope envelope = msgCtx.getMessage().getSOAPPart().getEnvelope();

                // Header may or may not exist yet
                SOAPHeader header = envelope.getHeader();
                if (header == null)
                    header = envelope.addHeader();

                // Add WSS Usertoken Element Tree 
                final SOAPElement security = header.addChildElement("Security", "wsse",
                        "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
                final SOAPElement userToken = security.addChildElement("UsernameToken", "wsse");
                userToken.addChildElement("Username", "wsse").addTextNode("MyWSSUsername");
                userToken.addChildElement("Password", "wsse").addTextNode("MyWSSPassword");

            } catch (final Exception e) {
                LOG.error(e);
                return false;
            }
        }
        return true;
    }

    ...
    // Other required methods on interface need no guts
}


No comments: