JAX-WS - Добавление заголовков SOAP - PullRequest
30 голосов
/ 24 февраля 2010

Я пытаюсь создать автономный клиент для использования некоторых веб-сервисов. Я должен добавить свое имя пользователя и пароль в заголовок SOAP. Я попытался добавить учетные данные следующим образом:

OTSWebSvcsService service = new OTSWebSvcsService();
OTSWebSvcs port = service.getOTSWebSvcs();

BindingProvider prov = (BindingProvider)port;
prov.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, "myusername");
prov.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, "mypassword");

...

Когда я вызываю метод службы, я получаю следующее исключение:

com.ibm.wsspi.wssecurity.SoapSecurityException: WSEC5048E: One of "SOAP Header" elements required.

Что я делаю не так? Как добавить эти свойства в заголовок SOAP?

Отредактировано: я использовал JAX-WS 2.1, включенный в JDK6. Я сейчас использую JAX-WS 2.2. Теперь я получаю следующее исключение:

com.ibm.wsspi.wssecurity.SoapSecurityException: WSEC5509E: A security token whose type is [http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#UsernameToken] is required.

Как мне создать этот токен?

Ответы [ 9 ]

31 голосов
/ 09 декабря 2012

Извините за мой плохой английский. Данные можно передавать в заголовке SOAP (JaxWS) с помощью @WebParam (header = true), например:

@WebMethod(operationName = "SendRequest", action = "http://abcd.ru/")
@Oneway
public void sendRequest(
    @WebParam(name = "Message", targetNamespace = "http://abcd.ru/", partName = "Message")
    Data message,
    @WebParam(name = "ServiceHeader", targetNamespace = "http://abcd.ru/", header = true, partName = "ServiceHeader")
    Header serviceHeader);

Если вы хотите сгенерировать клиента с SOAP-заголовками, вам нужно использовать -XadditionalHeaders, например:

wsimport -keep -Xnocompile -XadditionalHeaders -Xdebug http://12.34.56.78:8080/TestHeaders/somewsdl?wsdl -d /home/evgeny/DEVELOPMENT/JAVA/gen

Если вам не нужен веб-сервис @Oneway, вы можете использовать Holder следующим образом:

@WebMethod(operationName = "SendRequest", action = "http://abcd.ru/")
public void sendRequest(
    @WebParam(name = "Message", targetNamespace = "http://abcd.ru/", partName = "Message")
    Data message,
    @WebParam(name = "ServiceHeader", targetNamespace = "http://abcd.ru/", header = true, partName = "ServiceHeader")
    Holder<Header> serviceHeader);
27 голосов
/ 24 февраля 2010

Не уверен на 100%, поскольку в вопросе отсутствуют некоторые детали, но если вы используете RI JAX-WS, обратите внимание на Добавление заголовков SOAP при отправке запросов :

Портативный способ сделать это состоит в том, что вы создаете SOAPHandler и беспорядок с SAAJ, но РИ обеспечивает лучший способ сделать это.

При создании прокси или рассылка объект, они реализуют BindingProvider интерфейс. Когда ты используйте JAX-WS RI, вы можете WSBindingProvider, который определяет еще несколько методов, предоставляемых только JAX-WS RI.

Этот интерфейс позволяет установить произвольный номер объекта заголовка, каждый из которых представляет заголовок SOAP. Вы можете реализовать это самостоятельно, если вы хочу, но, скорее всего, вы бы использовали один из фабричные методы, определенные на Headers класс для его создания.

import com.sun.xml.ws.developer.WSBindingProvider;

HelloPort port = helloService.getHelloPort();  // or something like that...
WSBindingProvider bp = (WSBindingProvider)port;

bp.setOutboundHeader(
  // simple string value as a header, like <simpleHeader>stringValue</simpleHeader>
  Headers.create(new QName("simpleHeader"),"stringValue"),
  // create a header from JAXB object
  Headers.create(jaxbContext,myJaxbObject)
);

Обновите ваш код и попробуйте снова. А если вы не используете JAX-WS RI, обновите свой вопрос и предоставьте дополнительную контекстную информацию.

Обновление: Похоже, что веб-служба, которую вы хотите вызвать, защищена с помощью WS-Security / UsernameTokens. Это немного отличается от вашего первоначального вопроса. В любом случае, чтобы настроить ваш клиент на отправку имен пользователей и паролей, я предлагаю проверить отличный пост Реализация профиля имени пользователя WS-Security для веб-служб на основе Metro (переход к шагу 4). Использование NetBeans для этого шага может сильно облегчить ситуацию.

7 голосов
/ 18 сентября 2012

Кроме того, если вы используете Maven для создания своего проекта, вам необходимо добавить следующую зависимость:

    <dependency>
        <groupId>com.sun.xml.ws</groupId>
        <artifactId>jaxws-rt</artifactId>
        <version>{currentversion}/version>
    </dependency>

Это дает вам класс com.sun.xml.ws.developer.WSBindingProvider.

Ссылка: https://mvnrepository.com/artifact/com.sun.xml.ws/jaxws-rt

2 голосов
/ 25 апреля 2016

Я добавляю этот ответ, потому что ни один из остальных не работал для меня.

Мне пришлось добавить Обработчик заголовка к Прокси :

import java.util.Set;
import java.util.TreeSet;

import javax.xml.namespace.QName;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPFactory;
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 SOAPHeaderHandler implements SOAPHandler<SOAPMessageContext> {

    private final String authenticatedToken;

    public SOAPHeaderHandler(String authenticatedToken) {
        this.authenticatedToken = authenticatedToken;
    }

    public boolean handleMessage(SOAPMessageContext context) {
        Boolean outboundProperty =
                (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
        if (outboundProperty.booleanValue()) {
            try {
                SOAPEnvelope envelope = context.getMessage().getSOAPPart().getEnvelope();
                SOAPFactory factory = SOAPFactory.newInstance();
                String prefix = "urn";
                String uri = "urn:xxxx";
                SOAPElement securityElem =
                        factory.createElement("Element", prefix, uri);
                SOAPElement tokenElem =
                        factory.createElement("Element2", prefix, uri);
                tokenElem.addTextNode(authenticatedToken);
                securityElem.addChildElement(tokenElem);
                SOAPHeader header = envelope.addHeader();
                header.addChildElement(securityElem);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            // inbound
        }
        return true;
    }

    public Set<QName> getHeaders() {
        return new TreeSet();
    }

    public boolean handleFault(SOAPMessageContext context) {
        return false;
    }

    public void close(MessageContext context) {
        //
    }
}

В прокси я просто добавляю обработчик:

BindingProvider bp =(BindingProvider)basicHttpBindingAuthentication;
bp.getBinding().getHandlerChain().add(new SOAPHeaderHandler(authenticatedToken));
bp.getBinding().getHandlerChain().add(new SOAPLoggingHandler());
2 голосов
/ 11 марта 2016

Используйте maven и плагин jaxws-maven-plugin . это создаст клиента веб-службы. Убедитесь, что для xadditionalHeaders установлено значение true. Это сгенерирует методы с заголовками.

1 голос
/ 22 июня 2017

вы можете добавить имя пользователя и пароль к заголовку SOAP

BindingProvider prov = (BindingProvider)port;
prov.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
                "your end point"));
Map<String, List<String>> headers = new HashMap<String, List<String>>();
prov.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, "myusername");
prov.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, "mypassword");
prov.getRequestContext().put(MessageContext.HTTP_REQUEST_HEADERS, headers);
0 голосов
/ 17 октября 2018

Лучший вариант (для моего конечно) это сделать yourserfl. Это означает, что вы можете программно изменять все части сообщения SOAP

Binding binding = prov.getBinding();
   List<Handler> handlerChain = binding.getHandlerChain();
    handlerChain.add( new ModifyMessageHandler() );
    binding.setHandlerChain( handlerChain ); 

И источник ModifyMessageHandler может быть

@Override
public boolean handleMessage( SOAPMessageContext context )
{
    SOAPMessage msg = context.getMessage(); 
    try
    {

        SOAPEnvelope envelope = msg.getSOAPPart().getEnvelope();
        SOAPHeader header = envelope.addHeader();
        SOAPElement ele = header.addChildElement( new QName( "http://uri", "name_of_header" ) );
        ele.addTextNode( "value_of_header" );
        ele = header.addChildElement( new QName( "http://uri", "name_of_header" ) );
        ele.addTextNode( "value_of_header" );
        ele = header.addChildElement( new QName( "http://uri", "name_of_header" ) );
        ele.addTextNode( "value_of_header" );

...

Надеюсь, это поможет вам

0 голосов
/ 04 октября 2018

Я боролся со всеми ответами здесь, начиная с Решение Паскаля , которое усложняется, если компилятор Java больше не связывается с rt.jar по умолчанию (а использование внутренних классов делает его специфичнымреализация во время выполнения).

Ответ от edubriguenti приблизил меня.Однако способ, которым обработчик подключен в последнем фрагменте кода, не работал для меня - он никогда не вызывался.

В итоге я использовал вариант его класса обработчика, но связал его с javax.xml.ws.Service пример как это:

Service service = Service.create(url, qname); service.setHandlerResolver( portInfo -> Collections.singletonList(new SOAPHeaderHandler(handlerArgs)) );

0 голосов
/ 04 февраля 2017

В jaxws-rt-2.2.10-ources.jar!\com\sun\xml\ws\transport\http\client\HttpTransportPipe.java:

public Packet process(Packet request) {
        Map<String, List<String>> userHeaders = (Map<String, List<String>>) request.invocationProperties.get(MessageContext.HTTP_REQUEST_HEADERS);
        if (userHeaders != null) {
            reqHeaders.putAll(userHeaders);

Таким образом, Map<String, List<String>> из requestContext с ключом MessageContext.HTTP_REQUEST_HEADERS будет скопировано в заголовки SOAP. Пример Аутентификации приложения с помощью JAX-WS через заголовки

Ключи

BindingProvider.USERNAME_PROPERTY и BindingProvider.PASSWORD_PROPERTY обрабатываются особым образом в HttpTransportPipe.addBasicAuth(), добавляя стандартный базовый заголовок Authorization.

См. Также Контекст сообщения в JAX-WS

...