2010-08-23 20 views
6

Llamo a un servicio que no pertenece a WCF desde un cliente WCF. El cliente WCF incluye el atributo de encabezado "MustUnderstand" establecido en "1". He aquí una petición SOAP típica:Cómo modificar un MustUnderstand del encabezado del mensaje WCF utilizando ClientInspector

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> 
<s:Header> 
    <o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> 
     <u:Timestamp u:Id="_0"> 
      <u:Created>2010-08-23T20:48:52.680Z</u:Created> 
      <u:Expires>2010-08-23T20:53:52.680Z</u:Expires> 
     </u:Timestamp> 
     <o:UsernameToken u:Id="uuid-72ea0c0a-43aa-43b2-bed7-c2da13624105-1"> 
      <o:Username>blablabla</o:Username> 
      <o:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">blablabla</o:Password> 
     </o:UsernameToken> 
    </o:Security> 
</s:Header> 
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
    <HeartbeatRequest xmlns="http://removed"> 
     <DateTime xmlns="">8/23/2010 4:48:51 PM</DateTime> 
     <Message xmlns="">123</Message> 
    </HeartbeatRequest> 
</s:Body> 

Ahora, aquí está la respuesta que regrese para esto.

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 
<soapenv:Header> 
    <Misunderstood qname="o:Security" xmlns="http://www.w3.org/2002/06/soap-faults" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" /> 
</soapenv:Header> 
<soapenv:Body> 
    <soapenv:Fault xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"> 
     <faultcode>soapenv:MustUnderstand</faultcode> 
     <faultstring>WSWS3173E: Error: Did not understand &quot;MustUnderstand&quot; header(s):{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd}Security</faultstring> 
    </soapenv:Fault> 
</soapenv:Body> 

Nota la parte sobre MustUnderstand no ser comprendido.

El propietario de este servicio ha indicado que permiten elementos que tienen el prefijo de espacio de nombres WSSE pero no están realmente en el XSD, y realizan algún otro procesamiento que les impediría aceptar MustUnderstand = "1", así que tengo que encuentra una forma de enviar mensajes con MustUnderstand = "0".

Traté de cambiar esto en el MessageContract para el cliente proxy utilizando un atributo MessageHeader, pero eso no ayudó.

A continuación, implementé un inspector de mensajes de cliente personalizado. He creado clases por MSDN para un comportamiento personalizado elemento de extensión y un IEndpointBehavior, éstos son desdeñables, pero aquí para completar:

public class ExClientBehavior : IEndpointBehavior 
{ 
    #region IEndpointBehavior Members 

    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) 
    { 
     // no op 
    } 

    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) 
    { 
     ExInspector inspector = new ExInspector(); 
     clientRuntime.MessageInspectors.Add(inspector); 
    } 

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) 
    { 
     // no op 
    } 

    public void Validate(ServiceEndpoint endpoint) 
    { 
     // no op 
    } 

    #endregion 
} 


    public class ExClientBehaviorExtensionElement : BehaviorExtensionElement 
{ 
    public override Type BehaviorType 
    { 
     get { return typeof(ExClientBehavior); } 
    } 

    protected override object CreateBehavior() 
    { 
     return new ExClientBehavior(); 
    } 
} 

y ahora el inspector real:

public class ExInspector : IClientMessageInspector 
{ 

    #region IClientMessageInspector Members 

    public void AfterReceiveReply(ref Message reply, object correlationState) 
    { 
     // no op 
     return; 
    } 

    public object BeforeSendRequest(ref Message request, IClientChannel channel) 
    { 
     MessageBuffer buffer = request.CreateBufferedCopy(int.MaxValue); 

     Message newMessage = buffer.CreateMessage(); 

     newMessage.Headers.RemoveAt(0); 

     newMessage.Headers.Add(MessageHeader.CreateHeader 
      (
       request.Headers[0].Name, 
       request.Headers[0].Namespace, 
       string.Empty, 
       false, 
       string.Empty, 
       request.Headers[0].Relay 
      ) 
     ); 

     request = newMessage; 

     return null; 
    } 

    #endregion 
} 

Como se puede ver, estoy creando una nueva solicitud mediante copia en búfer, luego eliminando el encabezado de seguridad (solo hay un encabezado) y agregando uno nuevo con MustUnderstand establecido en falso (¿por qué estoy haciendo esto? MessageHeader.MustUnderstand es de solo lectura). Establecí un punto de interrupción en este método y, de hecho, se agrega el nuevo encabezado, el nuevoMessage se escribe de nuevo en la solicitud y tanto newMessage.Headers [0] .MustUnderstand como request.Headers [0] .MustUnderstand son falsos en el fin de este método

Sin embargo, el mensaje que se envía al servicio todavía incluye MustUnderstand = "1" en el encabezado !!!!!

Aquí está el app.config que incluye el comportamiento anterior:

<configuration> 
<system.serviceModel> 
    <bindings> 
     <basicHttpBinding> 
      <binding name="WebServiceSOAP" closeTimeout="00:01:00" openTimeout="00:01:00" 
        receiveTimeout="00:10:00" sendTimeout="00:01:00" allowCookies="false" 
        bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" 
        maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" 
        messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered" 
        useDefaultWebProxy="true"> 
       <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384" /> 
       <security mode="TransportWithMessageCredential"> 
        <transport clientCredentialType="Basic" proxyCredentialType="None" realm="" /> 
        <message clientCredentialType="UserName" algorithmSuite="Default" /> 
       </security> 
      </binding> 
     </basicHttpBinding> 
    </bindings> 
    <client> 
     <endpoint 
       address="https://removed" 
       behaviorConfiguration="ovrExClientBehavior" 
       binding="basicHttpBinding" 
       bindingConfiguration="WebServiceSOAP" 
       contract="EWebService.EWebService" 
       name="WebServiceSOAP" /> 
    </client> 
    <extensions> 
     <behaviorExtensions> 
      <add name="exClientBehavior" type="ExMessageInspector.ExClientBehaviorExtensionElement, ExMessageInspector, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/> 
     </behaviorExtensions> 
    </extensions> 
    <behaviors> 
     <endpointBehaviors> 
      <behavior name="ovrExClientBehavior"> 
       <exClientBehavior /> 
      </behavior> 
     </endpointBehaviors> 
    </behaviors> 
</system.serviceModel> 

Así que mi pregunta: ¿es posible cambiar MustUnderstand en un mensaje de salida como la de arriba, o de una manera similar? ¿O se vuelve a cambiar por la fuerza a True más adelante en la tubería, después de que el inspector reemplaza el encabezado de seguridad?

Nota: el propietario del servicio dice que solo conocen otra organización que consume este servicio en .NET, y que ese consumidor tuvo que descartar esencialmente WCF y WSE y crear mensajes SOAP y gestionar las respuestas desde cero , probablemente usando POX POST o algo así. Realmente preferiríamos evitar esto, ya que necesitamos llamar a varias operaciones en el servicio.

Además, debemos preservar intactos el cuerpo y las propiedades del mensaje.

¡Cualquier ayuda sería muy apreciada!

Respuesta

5

Me pregunto por qué existen estándares para la interoperabilidad si los proveedores no los siguen. Si el inspector de clientes no funciona, puede intentar implementar un codificador de mensajes personalizado y modificar el encabezado allí.

Editar:

La pregunta aquí es ¿por qué enviar las credenciales de usuario para la autenticación si en el mismo tiempo que declara que el servicio no tiene que entender el encabezado con credenciales = no tiene que usar ellos. ¿Realmente los necesitas?

Existen otros enfoques que dependen de sus requisitos. ¿Necesitas marca de tiempo? ¿La marca de tiempo está marcada en el servidor? ¿Tiene un usuario único para todas las llamadas o tiene que diferenciar usuarios entre llamadas?

Si no necesita marca de tiempo o la marca de tiempo no está marcada y tiene solo un nombre de usuario y contraseña, la manera más fácil es no usar el modo de seguridad TranportWithMessageCredential. Utilice el transporte puro en vez y lugar Descripción cabecera dentro de la configuración de extremo de cliente como:

<client> 
    <endpoint address="https://removed" binding="basicHttpBinding" bindingConfiguration="WebServiceSOPA" contract="EWebService.EWebService" name="WebServiceSOAP"> 
    <headers> 
    <wsse:Security s:mustUnderstand="0" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> 
     <wsse:UsernameToken wsu:Id="SecurityToken-3f7f983f-66ce-480d-bce6-170632d33f92" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> 
     <wsse:Username>User</wsse:Username> 
     <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">Pwd123</wsse:Password> 
     </wsse:UsernameToken> 
    </wsse:Security> 
    </headers> 
    </endpoint> 
    </client> 

Si tiene varios nombres de usuario o si necesita marca de tiempo real con datos reales puede utilizar mismo enfoque pero en lugar de la configuración estática puede crear personalizada encabezado en el código y evitar la seguridad WCF. Esto se puede hacer usando el inspector de mensajes.

+0

Desafortunadamente sí, este proveedor combina MustUnderstand = 0 con la obligación del nombre de usuario/contraseña (estático) en el encabezado. Si omitimos eso, obtenemos un error de actor faltante (es decir, elemento faltante). No creo que se requiera la marca de tiempo. Hm - Probaré el enfoque de configuración de punto final y comentaré. – pelazem

+0

Bien, gracias Ladislav! Intenté su enfoque, y como tenemos la "suerte" de tener un nombre de usuario/contraseña estáticos, ir con Transporte solo por seguridad y el encabezado estático tal como lo diseñó, ¡funciona! Muchas gracias. – pelazem

+0

Ladislav, gracias por su aproximación, pero no pude usarlo. Como agregué este encabezado en la configuración del punto final, tengo los errores de compilación 's: el prefijo no está definido'. ¿Puede dar un comentario sobre el espacio de nombres que se debe utilizar en este encabezado? –

Cuestiones relacionadas