JAX-WS Password Type PasswordText
Asked Answered
M

2

21

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.

Thanks.

Moquette answered 22/6, 2010 at 18:48 Comment(0)
H
37

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!

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
}

I've made a few assumptions here, but hopefully it'll get you going!

Kind regards.

Hydroxylamine answered 25/6, 2010 at 12:10 Comment(4)
Thanks. This worked perfectly and your answer was almost impossible to find by Googling, so allow me to add some keywords. JAX-WS WSSEBanjo
When using this code I got the following exception in by log: com.sun.xml.internal.messaging.saaj.soap.impl.ElementImpl addChildElement SEVERE: SAAJ0101: Parent of a SOAPBodyElement has to be a SOAPBody.Foregone
This doesn't always work - you need to use a factory object to generate the elements to avoid the dreaded SAAJ0101 error. See @cleison answer instead.Telegenic
@Telegenic Well, this is 8 years old now - I would hope that things have moved on a bit since my original answer!Hydroxylamine
U
8

If you implement SOAPHandler interface, the method msgCtx.getMessage() will render the entire XML, and if you are working with big files you will have Out of Memory errors. I tested with UsernameToken authentication on JAX-WS client and it works:

String SECURITY_NS = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
String PASSWORD_TYPE = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText";
String AUTH_PREFIX = "wss";

MyService service = new MyService();
MyServicePort port = service.getMyServicePort();

try {
    SOAPFactory soapFactory = SOAPFactory.newInstance();
    SOAPElement security = soapFactory.createElement("Security", AUTH_PREFIX, SECURITY_NS);
    SOAPElement uToken = soapFactory.createElement("UsernameToken", AUTH_PREFIX, SECURITY_NS);
    SOAPElement username = soapFactory.createElement("Username", AUTH_PREFIX, SECURITY_NS);
    username.addTextNode("username");

    SOAPElement pass = soapFactory.createElement("Password", AUTH_PREFIX, SECURITY_NS);
    pass.addAttribute(new QName("Type"), PASSWORD_TYPE);
    pass.addTextNode("password");

    uToken.addChildElement(username);
    uToken.addChildElement(pass);
    security.addChildElement(uToken);

    Header header = Headers.create(security);
    ((WSBindingProvider) port).setOutboundHeaders(header);

    // now, call webservice

} catch (SOAPException ex) {
    ex.printStackTrace();
}

Edit: You should add the "rt.jar" from jre to classpath.

Unbalance answered 6/6, 2013 at 20:56 Comment(2)
I tries this but im getting javax.xml.ws.WebServiceException: java.lang.ClassCastException: com.sun.xml.ws.message.DOMHeader cannot be cast to com.sun.xml.ws.security.opt.impl.outgoing.SecurityHeader at com.sun.xml.wss.jaxws.impl.SecurityClientTube.processRequest(SecurityClientTube.java:250) at com.sun.xml.ws.api.pipe.Fiber.__doRun(Fiber.java:1063)Fertilizer
i think it is related to having the metro ws jar in the classpath- but i need that oneFertilizer

© 2022 - 2024 — McMap. All rights reserved.