com.sun.xml.ws.message.saaj.SAAJHeader cannot be c

2019-05-02 01:33发布

问题:

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!

回答1:

When I was solving similar issue, this thread really helped me.

The problem is when you have Metro (Oracle's WS stack) libraries on classpath, they are autodetected and included in the WS processing. One functionality is transparent authentication / security handling, when endpoint policy is present in WSDL. Unfortunatelly this security handling is not

I was not able to change the WSDL. The solution for me was removing the unwanted JAR (wsit-rt) from the classpath.



回答2:

Old question, but I had currently the same problem. The solution was to remove additional security headers I had included manually. So if you run into this problem, check if you have any handlers (SOAPHandler) writing any headers into your soap message <Security> header part.