【问题标题】:How to modify a WCF message header's MustUnderstand using ClientInspector如何使用 ClientInspector 修改 WCF 消息头的 MustUnderstand
【发布时间】:2010-08-23 21:11:56
【问题描述】:

我正在从 WCF 客户端调用非 WCF 服务。 WCF 客户端包括设置为“1”的“MustUnderstand”标头属性。这是一个典型的 SOAP 请求:

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

现在,这是我收到的回复。

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

注意关于 MustUnderstand 的部分没有被理解。

此服务的所有者表示他们允许具有 WSSE 命名空间前缀但实际上不在 XSD 中的元素,并执行一些其他处理以阻止他们接受 MustUnderstand="1" 所以我必须找到一种使用 MustUnderstand="0" 发送消息的方法。

我尝试使用 MessageHeader 属性在代理客户端的 MessageContract 中更改此设置,但这没有帮助。

接下来,我实现了一个自定义客户端消息检查器。我为自定义行为扩展元素和 IEndpointBehavior 为每个 MSDN 创建了类,这些都是微不足道的,但为了完整起见:

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

现在是真正的 Inspector:

    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
}

如您所见,我正在通过缓冲副本创建一个新请求,然后删除安全标头(只有一个标头)并添加一个将 MustUnderstand 设置为 false 的新请求(我为什么要这样做?MessageHeader.MustUnderstand 是只读)。我在此方法中设置了一个断点,实际上,添加了新标头,将 newMessage 写回到请求中,并且 newMessage.Headers[0].MustUnderstand 和 request.Headers[0].MustUnderstand 在该方法结束。

但是,发送到服务的消息仍然在标头中包含 MustUnderstand="1" !!!!!

这是包含上述行为的 app.config:

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

所以我的问题是:是否可以像上面那样或以类似的方式在传出消息上更改 MustUnderstand?还是在检查器替换安全标头后,在管道中稍后将其强制更改回 true?

注意:服务所有者说他们只知道其他组织在 .NET 中使用此服务,并且该消费者必须基本上丢弃 WCF 和 WSE 并创建 SOAP 消息 - 并处理回复 - 从头开始​​,可能使用 POX POST 或类似的东西。我们真的希望避免这种情况,因为我们需要在服务上调用许多操作。

此外,我们需要完整地保留消息的正文和属性。

任何帮助将不胜感激!

【问题讨论】:

    标签: c# .net wcf web-services soap


    【解决方案1】:

    我想知道如果供应商不遵循互操作性标准,为什么还会存在这些标准。如果客户端检查器不起作用,您可以尝试实现自定义消息编码器并在那里修改标头。

    编辑:

    这里的问题是,如果您同时声明该服务不必理解带有凭据的标头 = 不必使用它们,那么为什么要发送用户凭据进行身份验证。你真的需要它们吗?

    还有其他方法取决于您的要求。你需要时间戳吗?是否在服务器上检查时间戳?您是所有呼叫都只有一个用户,还是必须在呼叫之间区分用户?

    如果您不需要时间戳或未检查时间戳并且您只有一个用户名和密码,最简单的方法是不使用 TranportWithMessageCredential 安全模式。改用纯传输并将标头描述放在客户端端点配置中,例如:

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

    如果您有多个用户名,或者如果您需要带有实际数据的真实时间戳,您可以使用相同的方法,但您可以在代码中创建自定义标头并避免 WCF 安全性,而不是静态配置。这可以使用消息检查器来完成。

    【讨论】:

    • 不幸的是,该供应商将 MustUnderstand=0 与仍然强制在标头中使用(静态)用户名/密码相结合。如果我们忽略它,我们会返回一个缺少 actor(即缺少元素)的错误。我不认为时间戳是必需的。嗯 - 我会尝试端点配置方法并发表评论。
    • 好的,谢谢拉迪斯拉夫!我尝试了您的方法,并且由于我们“幸运”地拥有静态用户名/密码,因此仅使用 Transport 是为了安全,并且在您布置时使用静态标头-有效!非常感谢。
    • Ladislav,感谢您的方法,但我无法使用它。由于我在端点配置中添加了此标头,因此出现编译错误s: prefix isn't defined。您能否就应在此标头中使用的命名空间发表评论。
    • 在代码示例中,它看起来像是绑定到 xmlns:s="schemas.xmlsoap.org/soap/envelope"
    • 谢谢,我已经尝试解决这个问题一段时间了。
    猜你喜欢
    • 2011-05-09
    • 1970-01-01
    • 2011-02-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-12
    • 2011-05-25
    相关资源
    最近更新 更多