web-dev-qa-db-fra.com

Comment communiquer correctement le WSSE Usernametoken pour SOAP webservice

J'essaie de consommer un service Web via son wsdl correspondant. Ce service dépend d'une authentification conforme à Web Services Security Basic Security Profile 1. y compris l'espace de noms xmls correct de http://docs.oasis-open.org/wss/2004/01 /oasis-200401wss-wssecurity-secext-1.0.xsd doit être inclus dans la demande.

Exemple:

<wsse:UsernameToken xmlns:wsse='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd' >
   <wsse:Username>
      Bob
   </wsse:Username>
   <wsse:Password Type='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText'>
      1234
   </wsse:Password>
</wsse:UsernameToken>

Mes premières tentatives ont été dans le sens de Add Service Reference ciblant le wsdl et à partir des proxys générés les utilisant comme tels

ServicePointManager.ServerCertificateValidationCallback = 
    (object s, X509Certificate certificate, X509Chain chain,
                     SslPolicyErrors sslPolicyErrors) => true;

var basicHttpBinding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
basicHttpBinding.Security.Transport.ClientCredentialType = 
                                                HttpClientCredentialType.Basic;

var endpoint = new EndpointAddress("https://secure-ausomxana.crmondemand.com/..."

using (var client = new ContactClient(basicHttpBinding, endpoint))
{

    var credential = client.ClientCredentials.UserName;
    credential.UserName = "bob";
    credential.Password = "1234";

    var input = ...    
    var output = client.ContactQueryPage(input);
}

Cependant, en essayant d'interroger les messages SOAP avec Fiddler, je constate qu'aucun élément UsernameToken n'a été ajouté.

Quelle est la bonne façon de remplir ce contrat?

Edit: suite à la réponse de @John Saunders j'ai essayé de modifier mon code pour utiliser un wsHttpBinding

var wsHttpBinding = new WSHttpBinding(SecurityMode.Transport);
wsHttpBinding.Security.Transport.ClientCredentialType =
                                         HttpClientCredentialType.Basic;

En utilisant cette liaison, le message SOAP devient

<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
  <s:Header>
    <a:Action s:mustUnderstand="1">document/urn:crmondemand/ws/ecbs/contact/10/2004:ContactQueryPage</a:Action>
    <a:MessageID>urn:uuid:17807f44-1fcasfdsfd</a:MessageID>
    <a:ReplyTo>
      <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
    </a:ReplyTo>
    <a:To s:mustUnderstand="1">https://secure-ausomxana.crmondemand.com/Services/Integration</a:To>
  </s:Header>
  <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <ContactQueryPage_Input xmlns="urn:crmondemand/ws/ecbs/contact/10/2004">
      <ListOfContact xmlns="urn:/crmondemand/xml/Contact/Query">
        <Contact>
          <Id>1-asdfd</Id>
        </Contact>
      </ListOfContact>
    </ContactQueryPage_Input>
  </s:Body>
</s:Envelope>

Cela ajoute l'élément Header, par opposition à wsse:UsernameToken L'élément de référence du message soap original utilisant BasicHttpBinding est

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <ContactQueryPage_Input xmlns="urn:crmondemand/ws/ecbs/contact/10/2004">
      <ListOfContact xmlns="urn:/crmondemand/xml/Contact/Query">
        <Contact>
          <Id>1-asdfds</Id>
        </Contact>
      </ListOfContact>
    </ContactQueryPage_Input>
  </s:Body>
</s:Envelope>

Si je change la liaison pour être

var wsHttpBinding = new WSHttpBinding(SecurityMode.TransportWithMessageCredential);
wsHttpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
wsHttpBinding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;

Le message SOAP que je reçois est

<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
  <s:Header>
    <a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/SCT</a:Action>
    <a:MessageID>urn:uuid:eeb75457-f29e-4c65-b4bf-b580da26e0c5</a:MessageID>
    <a:ReplyTo>
      <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
    </a:ReplyTo>
    <a:To s:mustUnderstand="1">https://secure-ausomxana.crmondemand.com/Services/Integration</a:To>
    <o:Security xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" s:mustUnderstand="1">
      <u:Timestamp u:Id="_0">
        <u:Created>2011-05-02T13:30:09.360Z</u:Created>
        <u:Expires>2011-05-02T13:35:09.360Z</u:Expires>
      </u:Timestamp>
      <o:UsernameToken u:Id="uuid-dc3605a0-6878-42f4-b1f2-37d5c04ed7b4-2">
        <o:Username>Bob</o:Username>
        <o:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">1234</o:Password>
      </o:UsernameToken>
    </o:Security>
  </s:Header>
  <s:Body>
    <t:RequestSecurityToken xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust">
      <t:TokenType>http://schemas.xmlsoap.org/ws/2005/02/sc/sct</t:TokenType>
      <t:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</t:RequestType>
      <t:Entropy>
        <t:BinarySecret u:Id="uuid-7195ad74-580b-4e52-9e2c-682e5a684345-1" Type="http://schemas.xmlsoap.org/ws/2005/02/trust/Nonce">bI4xuyKwZ8OkQYBRnz2LDNV+zhIOnl0nwP24yI1QAwA=</t:BinarySecret>
      </t:Entropy>
      <t:KeySize>256</t:KeySize>
    </t:RequestSecurityToken>
  </s:Body>
</s:Envelope>

Cela semble être très proche, mais cela semble avoir crypté le corps du message soap, ce que je ne veux PAS que cela se produise.

Si je précise wsHttpBinding.Security.Message.ClientCredentialType = MessageCredentialType.UserName; en utilisant uniquement SecurityMode.Transport il revient à l'endroit où il dit son anonyme.

Quel est le dernier obstacle que je ne suis pas en mesure de franchir à ce sujet?

Solution finale: Je me suis dit que je posterais ceci au cas où cela aiderait quelqu'un, il n'y a pas vraiment beaucoup de différence ici, l'autre objet UserToken est enveloppé dans un nœud de sécurité qui est ce que mon fournisseur de services avait besoin et semble être la façon dont sa sortie de mes exemples précédents à partir de ce que j'ai pu obtenir généré.

<system.serviceModel>
  <bindings>    
    <basicHttpBinding>
      <binding name="Contact" closeTimeout="00:01:00" openTimeout="00:01:00"
          receiveTimeout="00:10:00" sendTimeout="00:01:00" allowCookies="false"
          bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
          maxBufferSize="524288" maxBufferPoolSize="524288" maxReceivedMessageSize="524288"
          messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
          useDefaultWebProxy="true">
        <readerQuotas maxDepth="32" maxStringContentLength="65536" maxArrayLength="131072"
            maxBytesPerRead="32768" maxNameTableCharCount="131072" />
        <security mode="Transport">
          <transport clientCredentialType="None" proxyCredentialType="None"
              realm="" />
          <message clientCredentialType="UserName" algorithmSuite="Default" />
        </security>
      </binding>         
    </basicHttpBinding>
  </bindings>
  <client>
    <endpoint address="https://secure-ausomxana.crmondemand.com/Services/Integration"
       binding="basicHttpBinding" bindingConfiguration="Contact"
       contract="OnDemandContactService.Contact" name="OnDemand.Contact.Endpoint">
      <headers>        
        <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
          <wsse:UsernameToken>
            <wsse:Username>USERNAME</wsse:Username>
            <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">PASSWORD</wsse:Password>
          </wsse:UsernameToken>
        </wsse:Security>
      </headers>
    </endpoint>
  </client>
</system.serviceModel>

Voir Avec C #, WCF SOAP consommateur qui utilise l'authentification en texte brut WSSE? pour savoir comment le configurer à l'aide de code et non de configuration

55
Chris Marisic

Si vous devez envoyer UserName via HTTPS, vous pouvez utiliser une approche standard (si votre WSDL est correctement défini, cela doit être créé automatiquement pour vous en ajoutant une référence de service):

<bindings>
  <basicHttpBinding>
    <binding name="secured">
      <security mode="TransportWithMessageCredential">
        <message clientCredentialType="UserName" />
      </security>
    </binding>
  </basicHttpBinding>
</bindings>
<client>
  <endpoint name="..." address="https://..." contract="..." binding="basicHttpBinding"
            bindingConfiguration="secured" />
</client>

Ar vous pouvez définir la liaison dans le code:

var basicHttpBinding = new BasicHttpBinding(BasicHttpSecurityMode.TransportWithMessageCredential);
basicHttpBinding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;

Vous allez définir des informations d'identification dans le proxy comme vous le faites maintenant:

client.ClientCredentials.UserName.UserName = "bob";
client.ClientCredentials.UserName.Password = "1234";

Si vous n'avez besoin que du profil UserNameToken sur HTTP sans aucune autre infrastructure WS-Security, l'approche la plus simple utilise ClearUserNameBinding .

Si vous avez besoin du même nom d'utilisateur et du même mot de passe pour toutes les demandes du client, vous pouvez utiliser un simple basicHttpBinding sans aucune sécurité et inclure l'en-tête statique de la configuration:

<client>
  <endpoint ...>
    <headers>
      <wsse:UsernameToken xmlns:wsse='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd' >
        <wsse:Username>Bob</wsse:Username>
        <wsse:Password Type='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText'>
           1234
        </wsse:Password>
      </wsse:UsernameToken>
    </headers>
  </endpoint>
</client> 

Si vous avez besoin de quelque chose de plus complexe, montrez la partie pertinente de WSDL (assertion de sécurité) ou un exemple de demande SOAP. Indiquez également si vous devez utiliser HTTP ou HTTPS.

58
Ladislav Mrnka

La réponse de @Ladislav est correcte. Cependant, je recevais un MessageSecurityException pour un SOAP 1.1 service Web que j'essayais d'utiliser. Suite ce billet de blog de Scott Hanselman, j'étais capable de le faire fonctionner. Voici le code que j'utilise:

    var oldBinding = new BasicHttpBinding(BasicHttpSecurityMode.TransportWithMessageCredential);
    oldBinding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;
    //remove the timestamp
    BindingElementCollection elements = oldBinding.CreateBindingElements();
    elements.Find<SecurityBindingElement>().IncludeTimestamp = false;
    //sets the content type to application/soap+xml
    elements.Find<TextMessageEncodingBindingElement>().MessageVersion = MessageVersion.Soap11;
    CustomBinding newBinding = new CustomBinding(elements);
7
Ionian316

Utilisez wsHttpBinding, pas basicHttpBinding.

En fait, vous devez simplement utiliser "Ajouter une référence de service" et pointer vers le WSDL du service.

3
John Saunders