【问题标题】:How to add header to SOAP request?如何将标头添加到 SOAP 请求?
【发布时间】:2013-01-25 13:40:50
【问题描述】:

我尝试通过java代码调用HTTPS SOAP web服务:

    URL url = new URL("https://somehost:8181/services/"SomeService?wsdl");
    QName qname = new QName("http://services.somehost.com/", "SomeService");
    Service service = Service.create(url, qname);
    SomeService port = service.getPort(SomeService .class);
    port.doSomething();

但得到异常:

threw an unexpected exception: javax.xml.ws.soap.SOAPFaultException: Security Requirements not met - No Security header in message

当我分析正确的请求样本时,我确定它必须包含标头:

 <S:Header>
  <To xmlns="http://www.w3.org/2005/08/addressing">http://somehost:8181/services/SomeService</To>
  <Action xmlns="http://www.w3.org/2005/08/addressing">https://somehost:8181/services/"SomeService/doSomethingRequest</Action>
  <ReplyTo xmlns="http://www.w3.org/2005/08/addressing">
     <Address>http://www.w3.org/2005/08/addressing/anonymous</Address>
  </ReplyTo>
  <MessageID xmlns="http://www.w3.org/2005/08/addressing">uuid:3428539e-d645-72ae-adc0-5423c1e68942</MessageID>
  <wsse:Security S:mustUnderstand="true">
     <wsu:Timestamp wsu:Id="_1" xmlns:ns14="http://docs.oasis-open.org/ws-sx/ws-secureconversation/200512" xmlns:ns13="http://schemas.xmlsoap.org/soap/envelope/">
        <wsu:Created>2013-01-15T16:36:30Z</wsu:Created>
        <wsu:Expires>2014-01-15T14:06:30Z</wsu:Expires>
     </wsu:Timestamp>
  </wsse:Security>

那么如何将此标头添加到我的 SOAP 请求中呢?

【问题讨论】:

    标签: java web-services jax-ws webservice-client


    【解决方案1】:

    我个人添加了两个类:HeaderHandler 和 HeaderHandlerResolver:

    import java.util.HashSet;
    import java.util.Set;
    
    import javax.xml.namespace.QName;
    import javax.xml.soap.SOAPElement;
    import javax.xml.soap.SOAPEnvelope;
    import javax.xml.soap.SOAPHeader;
    import javax.xml.soap.SOAPMessage;
    import javax.xml.ws.handler.MessageContext;
    import javax.xml.ws.handler.soap.SOAPHandler;
    import javax.xml.ws.handler.soap.SOAPMessageContext;
    
    
    public class HeaderHandler implements SOAPHandler<SOAPMessageContext> {
    
    public boolean handleMessage(SOAPMessageContext smc) {
    
        Boolean outboundProperty = (Boolean) smc.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
    
        if (outboundProperty.booleanValue()) {
    
            SOAPMessage message = smc.getMessage();
    
            try {
    
                SOAPEnvelope envelope = smc.getMessage().getSOAPPart().getEnvelope();
                SOAPHeader header = envelope.addHeader();
    
                SOAPElement security =
                        header.addChildElement("Security", "wsse", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
    
    
    
                SOAPElement usernameToken =
                        security.addChildElement("UsernameToken", "wsse");
                usernameToken.addAttribute(new QName("xmlns:wsu"), "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");
    
                SOAPElement username =
                        usernameToken.addChildElement("Username", "wsse");
                username.addTextNode("test");
    
                SOAPElement password =
                        usernameToken.addChildElement("Password", "wsse");
                password.setAttribute("Type", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText");
               password.addTextNode("test321");
    
                //Print out the outbound SOAP message to System.out
                message.writeTo(System.out);
                System.out.println("");
    
            } catch (Exception e) {
                e.printStackTrace();
            }
    
        } else {
            try {
    
                //This handler does nothing with the response from the Web Service so
                //we just print out the SOAP message.
                SOAPMessage message = smc.getMessage();
                message.writeTo(System.out);
                System.out.println("");
    
            } catch (Exception ex) {
                ex.printStackTrace();
            } 
        }
    
    
        return outboundProperty;
    
    }
    
    public Set getHeaders() {
        // The code below is added on order to invoke Spring secured WS.
        // Otherwise,
        // http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd
        // won't be recognised 
        final QName securityHeader = new QName(
                "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd",
                "Security", "wsse");
    
        final HashSet headers = new HashSet();
        headers.add(securityHeader);
    
        return headers;
    }
    
    public boolean handleFault(SOAPMessageContext context) {
        //throw new UnsupportedOperationException("Not supported yet.");
        return true;
    }
    
    public void close(MessageContext context) {
    //throw new UnsupportedOperationException("Not supported yet.");
    }
    }
    

    import java.util.ArrayList;
    import java.util.List;
    import javax.xml.ws.handler.Handler;
    import javax.xml.ws.handler.HandlerResolver;
    import javax.xml.ws.handler.PortInfo;
    
    
    public class HeaderHandlerResolver implements HandlerResolver {
    
    public List<Handler> getHandlerChain(PortInfo portInfo) {
      List<Handler> handlerChain = new ArrayList<Handler>();
    
      HeaderHandler hh = new HeaderHandler();
    
      handlerChain.add(hh);
    
      return handlerChain;
       }
    }
    

    在 HeaderHandler 类中,您可以添加所需的凭据。 最后使用它们:

    HeaderHandlerResolver handlerResolver = new HeaderHandlerResolver();
    service.setHandlerResolver(handlerResolver);
    

    【讨论】:

    • 这正是我想要的。感谢您提供详尽的示例。
    • 我很高兴我的回答能有所帮助
    • 我想知道为什么 SOAP 会如此复杂!非常感谢。你拯救了我的一天:)
    • @LaabidiRaissi :我已按照您提到的步骤进行操作。该代码工作正常,但它从未在标题下附加安全元素。我已通过将出站 SOAP 消息打印到 System.out 来确认它。你能告诉我我错过了什么吗?我需要在信封中再次设置消息吗?在这之间我也尝试过那个,但没有运气。
    • 我发现 SOAPMessage 需要显式保存。添加message.save()后,就成功了。
    【解决方案2】:

    我已按照@LaabidiRaissi 提到的步骤进行操作。该代码工作正常,但它从未在标题下附加安全元素。我已经通过将出站 SOAP 消息打印到 System.out 来确认它。经过深入研究,我发现需要显式保存 SOAPMessage 以反映更新的消息头。

    soapMessage.saveChanges();
    

    更多参考 - Check this link

    【讨论】:

    • @Laabidi 的回答中的代码曾经在 Java 7 和 tomcat 7 上为我工作。升级到 Java 8 和 tomcat 8 后需要 soapMessage.saveChanges()。
    【解决方案3】:

    示例主类:

    package test;
    
    import java.util.ArrayList;
    import java.util.List;
    import javax.xml.ws.BindingProvider;
    import javax.xml.ws.handler.Handler;
    
    // next headers is generated from "NetBeans New Webservice Client"
    import sk.firma.wstest.definitions.*;
    import sk.firma.wstest.schemas.*;
    
    /**
     *
     * @author Jan
     */
    public class TestWSService {
    
        /**
         * @param args the command line arguments
         */
        public static void main(String[] args) {
    
            try {
    
                WsService service = new WsService();
                Ws port = service.getWsKsSoap11();
    
                // This is the block that apply the Ws Security to the request
                BindingProvider bindingProvider = (BindingProvider) port;
                @SuppressWarnings("rawtypes")
                List<Handler> handlerChain = new ArrayList<Handler>();
                handlerChain.add(new WSSecurityHeaderSOAPHandler("username", "password"));
                bindingProvider.getBinding().setHandlerChain(handlerChain);
    
                // Initialize and Run Service
                InVal inVal = new InVal();
                ReturnValue retVal = port.test(inVal);
    
            } catch (Exception e) {
                e.printStackTrace();
            }       
        }
    }
    

    现在是 WS-Security Header SOAP 处理程序:

    package test;
    
    import java.text.DateFormat;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.HashSet;
    import java.util.Set;
    import java.util.TimeZone;
    
    import javax.xml.namespace.QName;
    import javax.xml.soap.SOAPElement;
    import javax.xml.soap.SOAPEnvelope;
    import javax.xml.soap.SOAPHeader;
    import javax.xml.ws.handler.MessageContext;
    import javax.xml.ws.handler.soap.SOAPHandler;
    import javax.xml.ws.handler.soap.SOAPMessageContext;
    
    public class WSSecurityHeaderSOAPHandler implements SOAPHandler<SOAPMessageContext> {
    
        private static final String URL_WSSE_NAMESPACE = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
        private static final String URL_WSU_NAMESPACE = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd";
    
        private final String usernameText;
        private final String passwordText;
    
        public WSSecurityHeaderSOAPHandler(String usernameText, String passwordText) {
            this.usernameText = usernameText;
            this.passwordText = passwordText;
    
        }
    
        public String getCurrentDateTime() {
            /* e.g. 2001-10-13T09:00:00Z */
            final SimpleDateFormat FORMATTER_DATETIME_NO_MS = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
    
            DateFormat dfETC = FORMATTER_DATETIME_NO_MS;
            dfETC.setTimeZone(TimeZone.getTimeZone("CET"));
    
            StringBuffer dateETC = new StringBuffer(dfETC.format(new Date()));
            dateETC.append('Z');
            return dateETC.toString();
        }
    
        public String getCurrentDateTimePlusDelay(long delayInSeconds) {
            /* e.g. 2001-10-13T09:00:00Z */
            final SimpleDateFormat FORMATTER_DATETIME_NO_MS = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
    
            DateFormat dfETC = FORMATTER_DATETIME_NO_MS;
            dfETC.setTimeZone(TimeZone.getTimeZone("CET"));
            Date date = new Date();
    
            long timeInMsecs = date.getTime();
            date.setTime(timeInMsecs + delayInSeconds*1000L);
            StringBuffer dateETC = new StringBuffer(dfETC.format(date));
            dateETC.append('Z');
            return dateETC.toString();
        }
    
        @Override
        public boolean handleMessage(SOAPMessageContext soapMessageContext) {
    
            Boolean outboundProperty = (Boolean) soapMessageContext.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
    
            if (outboundProperty) {
    
                try {
                    SOAPEnvelope soapEnvelope = soapMessageContext.getMessage().getSOAPPart().getEnvelope();
    
                    SOAPHeader header = soapEnvelope.getHeader();
                    if (header == null) {
                        header = soapEnvelope.addHeader();
                    }
    
                    SOAPElement securityHeaderElement = header.addChildElement("Security", "wsse", URL_WSSE_NAMESPACE);
                    securityHeaderElement.addAttribute(soapEnvelope.createName("S:mustUnderstand"), "1");
    
                    // Add Timestamp element to "Security" soapHeaderElement
                    // Sample:  <u:Timestamp>
                    //              <u:Created>2011-10-13T08:20:01.183Z</u:Created>
                    //              <u:Expires>2011-10-13T17:25:01.183Z</u:Expires>
                    //          </u:Timestamp>
                    javax.xml.soap.Name timestampElementName = soapEnvelope.createName("Timestamp", "wsu", URL_WSU_NAMESPACE);
                    SOAPElement timestampSOAPElement = securityHeaderElement.addChildElement(timestampElementName);
                    String created = getCurrentDateTime(); 
                    String expires = getCurrentDateTimePlusDelay(60L*60L); /* 60 minutes delay */
    
                    // Add Created to Timestamp
                    SOAPElement createdSOAPElement = timestampSOAPElement
                            .addChildElement("Created"/* local name */, "wsu" /* prefix */, URL_WSU_NAMESPACE);
                    createdSOAPElement.addTextNode(created);
    
                    // Add Expires to Timestamp
                    SOAPElement expiresSOAPElement = timestampSOAPElement
                            .addChildElement("Expires"/* local name */, "wsu" /* prefix */,URL_WSU_NAMESPACE);
                    expiresSOAPElement.addTextNode(expires);
    
                    // Add usernameToken to "Security" soapHeaderElement
                    javax.xml.soap.Name usernameTokenElementName = soapEnvelope.createName("UsernameToken", "wsse", 
                                                URL_WSSE_NAMESPACE);
                    SOAPElement usernameTokenSOAPElement = securityHeaderElement.addChildElement(usernameTokenElementName);
    
                    // Add Username to usernameToken
                    SOAPElement userNameSOAPElement = usernameTokenSOAPElement
                            .addChildElement("Username"/* local name */, "wsse" /* prefix */, URL_WSSE_NAMESPACE);
                    userNameSOAPElement.addTextNode(this.usernameText);
    
                    // Add password to UsernameToken
                    javax.xml.soap.Name passwordElementName = soapEnvelope.createName("Password", "wsse", URL_WSSE_NAMESPACE);
                    SOAPElement passwordSOAPElement = usernameTokenSOAPElement.addChildElement(passwordElementName);
    
                    /* Add "Type" attribute to <Password> header element */
                    //passwordSOAPElement.addAttribute(soapEnvelope.createName("Type", "", ""), 
                    //      "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText");
                    passwordSOAPElement.addTextNode(this.passwordText);
    
                } catch (Exception e) {
                    throw new RuntimeException("Error on wsSecurityHandler: " + e.getMessage());
                }
    
            }
    
            return true;
        }
    
        @Override
        public void close(MessageContext context) {
            // TODO Auto-generated method stub
        }
    
        @Override
        public boolean handleFault(SOAPMessageContext context) {
            // TODO Auto-generated method stub
            return true;
        }
    
        @Override
        public Set<QName> getHeaders() {
            // throw new UnsupportedOperationException("Not supported yet.");
            final QName securityHeader = new QName("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", 
                                                        "Security", "wsse");
            final HashSet headers = new HashSet();
            headers.add(securityHeader);
    
            return headers;
        }
    
    }
    

    【讨论】:

    • @Laabidi Raissi 解决方案对我不起作用。但是这个有用!
    【解决方案4】:

    您还可以使用 Apache wss4j 轻松添加标头并加密您的密码。

    import org.apache.ws.security.WSConstants;
    import org.apache.ws.security.message.WSSecHeader;
    import org.apache.ws.security.message.WSSecUsernameToken;
    
    import javax.xml.namespace.QName;
    import javax.xml.soap.SOAPMessage;
    import javax.xml.soap.SOAPPart;
    import javax.xml.ws.handler.MessageContext;
    import javax.xml.ws.handler.soap.SOAPHandler;
    import javax.xml.ws.handler.soap.SOAPMessageContext;
    import java.io.ByteArrayOutputStream;
    import java.util.Set;
    
    public class WSSecurityHeaderSOAPHandler implements SOAPHandler<SOAPMessageContext> {
    
    
        private final String usernameText;
        private final String passwordText;
    
        public WSSecurityHeaderSOAPHandler(String usernameText, String passwordText) {
            this.usernameText = usernameText;
            this.passwordText = passwordText;
        }
    
        @Override
        public boolean handleMessage(SOAPMessageContext context) {
            Boolean outboundProperty = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
    
            if (outboundProperty.booleanValue()) {
    
                try {
                    SOAPMessage soapMessage = context.getMessage();
                    soapMessage.removeAllAttachments();
    
                    SOAPPart soappart = soapMessage.getSOAPPart();
                    WSSecHeader wsSecHeader = new WSSecHeader();
                    wsSecHeader.insertSecurityHeader(soappart);
                    WSSecUsernameToken token = new WSSecUsernameToken();
                    token.setPasswordType(WSConstants.PASSWORD_DIGEST);
                    token.setUserInfo(usernameText, passwordText);
                    token.build(soappart, wsSecHeader);
    
                    soapMessage.saveChanges();
                } catch (Exception e) {
                    throw new RuntimeException("Error on wsSecurityHandler: " + e.getMessage());
                }
    
            }
    
            return true;
        }
    
        @Override
        public boolean handleFault(SOAPMessageContext context) {
            return false;
        }
    
        @Override
        public void close(MessageContext context) {
    
        }
    
        @Override
        public Set<QName> getHeaders() {
            return null;
        }
    }
    

    您需要像这样更新您的请求:

    // This is the block that apply the Ws Security to the request
    BindingProvider bindingProvider = (BindingProvider) portType;
    List<Handler> handlerChain = new ArrayList<>();
    handlerChain.add(new WSSecurityHeaderSOAPHandler("username", "password"));
    bindingProvider.getBinding().setHandlerChain(handlerChain);
    

    Maven 依赖:

            <dependency>
                <groupId>org.apache.ws.security</groupId>
                <artifactId>wss4j</artifactId>
                <version>1.6.19</version>
            </dependency>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-10
      相关资源
      最近更新 更多