web-dev-qa-db-fra.com

JAX-WS - Ajout SOAP En-têtes

J'essaie de créer un client autonome pour utiliser certains services Web. Je dois ajouter mon nom d'utilisateur et mon mot de passe à l'en-tête SOAP. J'ai essayé d'ajouter les informations d'identification comme suit:

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");

...

Lorsque j'appelle une méthode sur le service, j'obtiens l'exception suivante:

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

Qu'est-ce que je fais mal? Comment pourrais-je ajouter ces propriétés à l'en-tête SOAP? 

Édité: J'utilisais JAX-WS 2.1 inclus dans JDK6. J'utilise maintenant JAX-WS 2.2. Je reçois maintenant l'exception suivante:

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.

Comment puis-je créer ce jeton?

29
Jordan Allan

Pas tout à fait sûr, certains détails manquent à la question, mais si vous utilisez RI JAX-WS, jetez un coup d'œil à Ajout d'en-têtes SOAP lors de l'envoi de demandes }:

La façon portable de le faire est que vous créez une SOAPHandler et un désordre avec SAAJ, mais le RI fournit un meilleur moyen de le faire.

Lorsque vous créez un proxy ou une répartition objet, ils implémentent BindingProvider interface. Lorsque vous utilisez le RI JAX-WS, vous pouvez rétrograder à WSBindingProvider qui définit un quelques méthodes supplémentaires fournies uniquement par le JAX-WS RI.

Cette interface vous permet de définir un nombre arbitraire d'objet Header, chacun représentant un en-tête SOAP. Vous pouvez le mettre en œuvre vous-même si vous voulez, mais vous utiliserez probablement l'un des les méthodes d'usine définies sur Headers classe pour en créer un. 

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

Mettez à jour votre code en conséquence et réessayez. Et si vous n'utilisez pas JAX-WS RI, veuillez mettre à jour votre question et fournir davantage d'informations contextuelles.

Update: Il semble que le service Web que vous souhaitez appeler soit sécurisé avec WS-Security/UsernameTokens. C'est un peu différent de votre question initiale. Quoi qu'il en soit, pour configurer votre client afin qu'il envoie les noms d'utilisateur et les mots de passe, je suggère de vérifier l'excellent message Implémentation du profil WS-Security UsernameToken pour les services Web Metro (passez à l'étape 4). L’utilisation de NetBeans pour cette étape peut grandement faciliter les choses.

27
Pascal Thivent

Désolé pour mon mauvais anglais. Les données peuvent être transférées dans l'en-tête SOAP (JaxWS) en utilisant @WebParam (header = true) comme ceci:

@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);

Si vous souhaitez générer un client avec des en-têtes SOAP, vous devez utiliser -XadditionalHeaders de la manière suivante:

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

Si vous n'avez pas besoin du service Web @Oneway, vous pouvez utiliser Titulaire de cette manière:

@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);
28
Evgeny

De plus, si vous utilisez Maven pour construire votre projet, vous devez ajouter la dépendance suivante:

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

Ceci vous fournit la classe com.Sun.xml.ws.developer.WSBindingProvider.

Lien: https://mvnrepository.com/artifact/com.Sun.xml.ws/jaxws-rt

7
cristianoms

Utilisez maven et le plugin jaxws-maven-plugin . cela générera un client de service Web. Assurez-vous que vous définissez xadditionalHeaders sur true. Cela générera des méthodes avec des entrées d'en-tête.

2
Eduardo Dennis

J'ajoute cette réponse car aucun des autres n'a fonctionné pour moi.

Je devais ajouter un Header Handler au Proxy :

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) {
        //
    }
}

Dans le proxy, je viens d'ajouter le gestionnaire:

BindingProvider bp =(BindingProvider)basicHttpBindingAuthentication;
bp.getBinding().getHandlerChain().add(new SOAPHeaderHandler(authenticatedToken));
bp.getBinding().getHandlerChain().add(new SOAPLoggingHandler());
1
edubriguenti

vous pouvez ajouter le nom d'utilisateur et le mot de passe à l'en-tête 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);
1
MahmoudPrime

Je me suis débattu avec toutes les réponses ici, en commençant par la solution de Pascal , qui devient de plus en plus difficile avec le compilateur Java qui ne lie plus par défaut avec rt.jar (et l'utilisation de classes internes le rend spécifique à cette implémentation d'exécution). 

La réponse de edubriguenti m'a rapproché. La façon dont le gestionnaire est connecté dans le dernier morceau de code ne m'a pas fonctionné, cependant - il n'a jamais été appelé.

J'ai fini par utiliser une variante de sa classe de gestionnaire, mais je l'ai intégrée dans l'instance javax.xml.ws.Service comme ceci:

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

0
Peter Becker

La meilleure option (pour moi bien sûr) est de le faire soi-même. Cela signifie que vous pouvez modifier par programme toutes les parties du message SOAP

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

Et la source ModifyMessageHandler pourrait être 

@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" );

...

J'espère que ceci vous aide

0
rumberomelo