Как добавить заголовок в запрос SOAP?

Я пытаюсь вызвать веб-службу HTTPS SOAP через java-код:

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>
</s:header>

Итак, как добавить этот заголовок в мой запрос SOAP?

3 ответа

Я лично добавляю два класса: 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.");
}
}
</soapmessagecontext>

и

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;
 }
}
</handler></handler></handler>

В классе HeaderHandler вы можете добавить необходимые учетные данные. Чтобы окончательно использовать их:

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


Я выполнил шаги, упомянутые @LaabidiRaissi. Код работает отлично, но никогда не добавляет элемент безопасности под заголовком. Я подтвердил это, распечатав исходящее сообщение SOAP на System.out. После глубоких исследований я обнаружил, что SOAPMessage необходимо явно сохранить для отражения обновленного заголовка сообщения.

soapMessage.saveChanges();

Для получения дополнительной информации - Проверьте эту ссылку


Вы также можете использовать 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;
 }
}
</qname></soapmessagecontext>

И вам нужно обновить свой запрос следующим образом:

// 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);
</handler>

Зависимость Maven:

<dependency>
 <groupid>org.apache.ws.security</groupid>
 <artifactid>wss4j</artifactid>
 <version>1.6.19</version>
 </dependency>

licensed under cc by-sa 3.0 with attribution.