2011-04-29 21 views
46

Estoy intentando consumir un servicio web a través de su wsdl correspondiente. Este servicio depende de la autenticación conforme a Web Services Security Basic Security Profile 1.0, incluido que debe incluirse en la solicitud el espacio de nombres xmls correcto de http://docs.oasis-open.org/wss/2004/01/oasis-200401wss-wssecurity-secext-1.0.xsd.Forma correcta de comunicarse Nombre de usuario de WSSE detectado para SOAP webservice

Ejemplo:

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

Mis primeros intentos fueron a lo largo de las líneas de Add Service Reference dirigidos a la WSDL y de los proxies generados usando como tales

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

Sin embargo intentar interrogar a los mensajes SOAP con Fiddler Veo que no se ha agregado ningún elemento UsernameToken.

¿Cuál es la forma correcta de cumplir este contrato?

Editar: después de la respuesta de @ John Saunders me trataron de alterar el código para utilizar un wsHttpBinding

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

El uso de esta unión se convierte en el mensaje SOAP

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

Esto agrega el encabezado Elemento, a diferencia del elemento wsse:UsernameToken como referencia, el mensaje de jabón original que utiliza BasicHttpBinding es

<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 cambio la encuadernación que se

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

El mensaje SOAP que salga es

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

Esto parece estar muy cerca sin embargo, esto parece tener realmente cifrada del cuerpo de la mensaje de jabón que es algo que NO quiero que suceda.

Si especifico wsHttpBinding.Security.Message.ClientCredentialType = MessageCredentialType.UserName; con solo usar SecurityMode.Transport vuelve a donde dice que es anónimo.

¿Cuál es el último obstáculo que no puedo aclarar sobre esto?

Solución Final: pensé que había puesto esto en caso que ayuda a alguien, no hay realmente muy diferente aquí otro objeto UserToken se envuelve en un nodo de Seguridad, que es lo que requiere el profesional del servicio y parece ser cómo su salida de mis ejemplos anteriores de lo que podría generar.

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

Ver With C#, WCF SOAP consumer that uses WSSE plain text authentication? para saber cómo configurarlo usando el código y no config

+0

¿Por qué establece la seguridad del transporte básico? ¿Necesita autenticación de transporte y mensaje? –

+0

¿Crees que podrías editar esto para mostrar una receta concisa? Es difícil superar la narración; No estoy exactamente claro en lo que estás agregando y restando a medida que la narración continúa. –

+0

@ArenCambre La versión concisa es que si esperas que un mensaje use 'wsse: Security' necesitas usar la asignación estática en el archivo web.config como tengo en mi solución final. Si necesita variar el nombre de usuario y la contraseña, hasta donde puedo decir que es SOL. –

Respuesta

49

Si necesita enviar Nombre de usuario a través de HTTPS se puede utilizar el enfoque estándar (si el WSDL se define correctamente esta debe ser creada de forma automática mediante la adición de referencia de servicio):

<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 puede definir la unión de código:

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

va a establecer credenciales de proxy como lo hace ahora:

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

Si solo necesita el perfil UserNameToken a través de HTTP sin ninguna otra infraestructura de WS-Security, el enfoque más simple es usar ClearUserNameBinding.

Si necesitas mismo nombre de usuario y contraseña para todas las solicitudes del cliente puede utilizar basicHttpBinding sencilla y sin ningún tipo de seguridad e incluir la cabecera estática de configuración:

<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 necesitas algo más compleja muestran la relevancia parte de WSDL (aserción de seguridad) o solicitud SOAP de muestra. También mencione si debe usar HTTP o HTTPS.

+0

Sí, este encabezado se usará en todas las solicitudes sin que cambie la información, aparte del requisito de enviar el nombre de usuario. La única otra especificación es que se accede al servicio a través de HTTPS. –

+0

@Chris: Agregué alguna configuración para HTTPS –

+0

Ladislav definitivamente esta parece ser la solución, más específicamente solo empujar la cadena de encabezado en la definición del punto final. Agradezco su ayuda ya que este tipo de configuración no es mi fuerte. Seguiré con el premio de recompensa después de hablar con el proveedor de servicios, parece que ahora puedo comunicarme con ellos correctamente.Sin embargo, su servicio se acaba de romper ahora y no mis mensajes de jabón. –

1

Uso wsHttpBinding, no basicHttpBinding.

De hecho, solo debe usar "Agregar referencia de servicio" y señalar el WSDL del servicio.

+1

No controlo el punto final y es probable que el punto final viole algunas series de infinitas permutaciones de los absurdos "estándares" de WSSE. –

3

@Ladislav La respuesta es correcta. Sin embargo, estaba obteniendo un MessageSecurityException para un servicio web SOAP 1.1 que estaba tratando de usar. Después de this blog post de Scott Hanselman, pude hacerlo funcionar. Este es el código que uso:

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

Al leer esta publicación de blog, ¿está seguro de que esto realmente responde mi pregunta original? Al leer la publicación, parece que 'MessageCredentialType.UserName' enviará ** solo ** un nombre de usuario, que sería útil para una autenticación de estilo ApiKey mientras viola los estándares de WS-Security. Nunca entendí por qué las personas usan estándares que los violan al instante, supongo que podría ser que los estándares WS- * son tan absurdamente arcanos que ninguna persona puede entenderlos razonablemente para seguirlos incluso si ellos también lo quieren decir. Los estándares WS- * hacen que SOA regrese media década. Los "microservicios" están mucho más cerca del propósito de SOA –

+0

En realidad, 'ClientCredentialType'' UserName' incluye propiedades de ** Nombre de usuario ** y ** Contraseña **. La respuesta de @ ladislav me ayudó a tomar el camino correcto. Solo necesitaba agregar algunos cambios más para que funcione para el servicio web al que estoy llamando. Publiqué esto como respuesta porque 1) puede ayudar a otros a tener el mismo problema, y ​​2) Sé que esto debería ser un comentario en su respuesta, pero el código no formatea bien en un comentario. Estoy de acuerdo en que los estándares WS- * son demasiado complejos. – Ionian316

+0

Esto me ha ahorrado mucho tiempo hoy. ¡Gracias! – Laurijssen

Cuestiones relacionadas