I'm trying to access a third party web service that requires me to create a security header passing in time information, user name, and password. I've scoured the web for working examples and have tried numerous ways. I'm trying to do this with just what's built into Java 6. I'm not sure what I'm doing wrong. After generating my web service client classes from the WSDL, I created the Handler below.
import java.util.Set;
import java.util.TimeZone;
import javax.xml.namespace.QName;
import javax.xml.soap.Node;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPFactory;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.Text;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
public class MyHeaderHandler implements SOAPHandler<SOAPMessageContext>
{
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 isRequest = (Boolean)context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if(isRequest)
{
try
{
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:dd.SSS'Z'");
formatter.setTimeZone(TimeZone.getTimeZone("GMT"));
java.util.Date created = new java.util.Date();
java.util.Date expires = new java.util.Date(created.getTime() + (5l * 60l * 1000l));
SOAPMessage soapMsg = context.getMessage();
SOAPEnvelope soapEnv = soapMsg.getSOAPPart().getEnvelope();
SOAPHeader soapHeader = soapEnv.getHeader();
if (soapHeader == null)
soapHeader = soapEnv.addHeader();
SOAPFactory factory = SOAPFactory.newInstance();
SOAPElement securityElem = factory.createElement("Security", "o", uri);
securityElem.addAttribute(QName.valueOf("s:mustUnderstand"), "0");
SOAPElement timestampElem = factory.createElement("Timestamp", "u", uta);
timestampElem.addAttribute(QName.valueOf("xmlns:u"), uta);
timestampElem.addAttribute(QName.valueOf("Id"), "_0");
SOAPElement elem = factory.createElement("Created", "u", uta);
elem.addTextNode(formatter.format(created));
timestampElem.addChildElement(elem);
elem = factory.createElement("Expires", "u", uta);
elem.addTextNode(formatter.format(expires));
timestampElem.addChildElement(elem);
securityElem.addChildElement(timestampElem);
SOAPElement usernameElem = factory.createElement("UsernameToken", "o", uri);
elem = factory.createElement("Username", "o", uri);
elem.addTextNode("xxxxx");
usernameElem.addChildElement(elem);
elem = factory.createElement("Password", "o", uri);
elem.addTextNode("xxxxx");
elem.addAttribute(QName.valueOf("Type"), ta);
usernameElem.addChildElement(elem);
securityElem.addChildElement(usernameElem);
soapHeader.addChildElement(securityElem);
}
catch(Exception e)
{
System.out.println("Handler error!!!! - " + e);
}
}
return true;
}
public boolean handleFault(SOAPMessageContext context)
{
return true;
}
public void close(MessageContext context)
{
}
public Set<QName> getHeaders()
{
return null;
}
}
Next, I coded my test program to attach the handler and try calling the web service.
import java.util.ArrayList;
import java.util.List;
import javax.xml.namespace.QName;
import javax.xml.ws.handler.Handler;
import javax.xml.ws.handler.HandlerResolver;
import javax.xml.ws.handler.PortInfo;
import org.tempuri.ServiceName;
import org.tempuri.IServiceName;
public class test
{
public static void main(String[] args)
throws Exception
{
ServiceName service = new ServiceName(new URL("https://url.to.service/services/ServiceName.svc?wsdl"), new QName("http://org.tempuri/", "ServiceName"));
service.setHandlerResolver(new HandlerResolver()
{
public List<Handler> getHandlerChain(PortInfo portInfo)
{
List<Handler> handlerList = new ArrayList<Handler>();
handlerList.add(new MyHeaderHandler());
return handlerList;
}
});
IServiceName binding = service.getBasicHttpBindingIServiceName();
ArrayLiist results = binding.getMyData("my parm");
System.out.println("Size: " + results.size());
}
}
When I run this, I get the following error at the line where i do binding.getMyData():
Exception in thread "main" java.lang.ClassCastException: com.sun.xml.ws.message.saaj.SAAJHeader cannot be cast to com.sun.xml.ws.security.opt.impl.outgoing.SecurityHeader
at com.sun.xml.ws.security.opt.impl.JAXBFilterProcessingContext.setJAXWSMessage(JAXBFilterProcessingContext.java:140)
at com.sun.xml.wss.jaxws.impl.SecurityPipeBase.secureOutboundMessage(SecurityPipeBase.java:389)
at com.sun.xml.wss.jaxws.impl.SecurityClientPipe.process(SecurityClientPipe.java:196)
at com.sun.xml.ws.api.pipe.helper.PipeAdapter.processRequest(PipeAdapter.java:115)
at com.sun.xml.ws.api.pipe.Fiber.__doRun(Fiber.java:595)
at com.sun.xml.ws.api.pipe.Fiber._doRun(Fiber.java:554)
at com.sun.xml.ws.api.pipe.Fiber.doRun(Fiber.java:539)
at com.sun.xml.ws.api.pipe.Fiber.runSync(Fiber.java:436)
at com.sun.xml.ws.client.Stub.process(Stub.java:248)
at com.sun.xml.ws.client.sei.SEIStub.doProcess(SEIStub.java:135)
at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:109)
at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:89)
at com.sun.xml.ws.client.sei.SEIStub.invoke(SEIStub.java:118)
at $Proxy40.getM(Unknown Source)
at test.main(test.java:30)
Every approach I try ends up at the same point. How do I get around this? I can't figure out how to create the SecurityHeader to place into the header. Any help would be greatly appreciated. A working example would be great.
Thanks!