【问题标题】:WCF Client - How to process or ignore a MustUnderstand header element?WCF 客户端 - 如何处理或忽略 MustUnderstand 标头元素?
【发布时间】:2012-04-12 04:17:51
【问题描述】:

我正在编写一个WCF 客户端,它使用非.Net Web 服务,使用WS-Security。服务的响应包含一个 Security 标头,其中 mustUnderstand 设置为 true。

使用 ServiceModelListener,我确实看到了从服务返回的实际数据。但是,WCF 客户端失败了,因为它没有处理 Security 标头。

<env:Header>
<wsse:Security env:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsu:Timestamp wsu:Id="timestamp">
<wsu:Created>2012-03-28T13:43:54.474Z</wsu:Created>
<wsu:Expires>2012-03-28T13:48:54.474Z</wsu:Expires>
</wsu:Timestamp>
</wsse:Security>
</env:Header>

WCF 客户端错误消息:

此邮件的收件人不理解命名空间“http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd”中的标头“Security”,导致邮件无法处理。此错误通常表明此消息的发送方启用了接收方无法处理的通信协议。请确保客户端绑定的配置与服务的绑定一致。

我的WCF 客户端不需要任何时间戳信息。有没有一种简单的方法来存根处理例程?我已经尝试过扩展 Response 类并添加一个 [MessageHeader] 属性。

编辑:

问另一种方式:我如何实现一个WCF 客户端,它接受标记为必须理解的自定义标头元素?

【问题讨论】:

    标签: wcf web-services jboss wcf-binding ws-security


    【解决方案1】:

    在处理支持 ONVIF 的 IP 摄像机的某些代码时遇到问题。摄像头正在发回 Nonce 和 Created in Security 元素,而 WCF 不喜欢它。最终使用 IClientMessageInspector 捕获响应,并将标头重新标记为 mustUnderstand=false。

     public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
        {
    
            //Some cameras produce WS-Security headers as a repsonse which contain a nonce and created date/time WCF doesn't like this for some reason.
            //The WS-Security element contains mustUnderstand="true". When WCF can't process the unrecoginzed elements it throw an exception. 
            // The code below searches for a WS-Security header. If one is found it copies the message body and all headers but the WS-Security header.
            // A new WS-Security header is then created with mustUnderstand=false and added into the new message. The proxy clients
            // will still receive the WS-Security header, just won't throw exceptions because of Nonce and Created elements in the header.
            if (reply.Headers.Count > 0)
            {
                //Have a WS-Security header?
                int secHeaderIndex = reply.Headers.FindHeader("Security", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
                if (secHeaderIndex < 0) { return; }
    
                //Our replacement message
                System.ServiceModel.Channels.Message cleanedMessage = null;
                //Copy the body
                cleanedMessage = Message.CreateMessage(reply.Version, "", reply.GetReaderAtBodyContents());
                //Create a new WS-Security header with mustUnmderstand=false
                MessageHeader newSecHeader = MessageHeader.CreateHeader("Security", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", reply.Headers[0], false);
                for (int x=0; x<reply.Headers.Count; x++)
                {
                    if (x == secHeaderIndex)
                    {//Don't copy the old WS-Security header
                        continue;
                    }
                    //Not a WS-Security header, copy to the new message.
                    cleanedMessage.Headers.CopyHeaderFrom(reply, x);
                }
                cleanedMessage.Headers.Add(newSecHeader);
                reply = cleanedMessage;
            }
        }
    

    【讨论】:

      【解决方案2】:

      我遇到了类似的问题。我不确定这是否有用。

      MSDN WCF 可扩展性

      http://blogs.msdn.com/b/carlosfigueira/archive/2011/04/19/wcf-extensibility-message-inspectors.aspx

      此处的设置是基于证书、Oracle 应用服务器 10g 和 .Net 来使用服务的。在尝试弄清楚请求和响应发生了什么时,使用 SOAPUi 非常有用。

      我没有尝试修改代码以使用 basicHttpBinding,但我使用 WSHttpBinding 作为代码中配置的基础。然后用了

       WSHttpBinding binding = new WSHttpBinding()
              {
                  CloseTimeout = new TimeSpan(0, 1, 0),
                  OpenTimeout = new TimeSpan(0, 1, 0),
                  SendTimeout = new TimeSpan(0, 1, 0),
                  AllowCookies = false,
                  BypassProxyOnLocal = false,
                  HostNameComparisonMode = HostNameComparisonMode.StrongWildcard,
                  MaxBufferPoolSize = 524288,
                  MaxReceivedMessageSize = 65536,
                  MessageEncoding = WSMessageEncoding.Text,
                  UseDefaultWebProxy = false,
                  ReaderQuotas = new System.Xml.XmlDictionaryReaderQuotas()
                  {
                      MaxDepth = 32,
                      MaxArrayLength = 16384,
                      MaxBytesPerRead = 4096,
                      MaxNameTableCharCount = 16384,
                      MaxStringContentLength = 8192
                  }
              };
              binding.Security.Mode = SecurityMode.Transport;
              binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
              binding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.None;
              binding.Security.Transport.Realm = string.Empty;
              binding.Security.Message.ClientCredentialType = MessageCredentialType.Certificate;
              binding.Security.Message.EstablishSecurityContext = true;
              binding.Security.Message.NegotiateServiceCredential = true;
      
              CustomBinding customBinding = new CustomBinding();
              BindingElementCollection collection = binding.CreateBindingElements();
      

      循环遍历 TextMessageEncodingBindingElement 以将 Soap11 和 AddressingVersion 设置为 None。

       foreach (BindingElement element in collection)
              {
                  if (typeof(TextMessageEncodingBindingElement) == element.GetType())
                  {
                      TextMessageEncodingBindingElement item = element as TextMessageEncodingBindingElement;
                      if (null != item)
                      {
                          item.MessageVersion = MessageVersion.CreateVersion(EnvelopeVersion.Soap11, AddressingVersion.None);
                          customBinding.Elements.Add(item);
                      }
                  }
                  else
                      customBinding.Elements.Add(element);
              }
      

      我使用了 ChannelFactory 并为 Message Inspector 添加了 EndPoint Behavior。 此时我已经控制了请求,我可以添加适当的标头并修改 Action 上的 mustUnderstand。

      使用 SOAPUi,我将 Message.ToString() 放入 SOAPUI 并测试了请求。一旦需要的项目被添加到请求中,然后确定 OAS 服务器没有回复所有必要的元素。使用消息检查器进行回复,我修改了消息以包含缺少的标题。我不记得在哪里找到了消息检查器的基本代码,但您需要修改代码才能正确使用它。

      对于我的示例,这里有一些 sn-ps。

      对于

      中的转换消息
       public object BeforeSendRequest
      

      我需要修改标头,因此我使用 for 循环抓取 XElement 并添加 OASIS 标头并添加了 To 标头。

      XNamespace xmlns = "http://schemas.xmlsoap.org/soap/envelope/";
                      XElement securityHeader = new XElement(
                          xmlns + "Security", 
                          new XAttribute(xmlns + "wsse", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"), 
                          new XAttribute(xmlns + "xmlns", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"), 
                          new XAttribute(xmlns + "mustUnderstand", "0"));
                      element.Add(securityHeader);
      

      我还必须修改 Action Header

       else if (localName.Equals("Action", StringComparison.InvariantCultureIgnoreCase))
                  {
                      foreach (XAttribute a in element.Attributes())
                      {
                          if (a.Name.LocalName == "mustUnderstand")
                              a.Value = "0";
                      }
                  }
      

      我的问题是服务没有回复操作标头

      所以在

       public void AfterReceiveReply
      

      我调用了我的 TransformReply 返回类型 Message ,如下所示。您可能需要修改 string.Empty 的值,但这只是一个示例。

      ...

       Message reply = Message.CreateMessage(message.Version, null, reader);
              reply.Headers.Add(MessageHeader.CreateHeader("Action", string.Empty, string.Empty, false));
              reply.Properties.CopyProperties(message.Properties);
      

      ...

      我真的建议使用 SOUPUI 之类的工具来弄乱信封并查看回复。如果您使用 SSL,则需要创建一个 cacert 文件并将其放在首选项的 SSLSettings 中。

      【讨论】:

      • 感谢亚当!看起来很有趣的信息。不过,我现在已经转而从事不同的咨询业务,因此无法再针对该系统进行尝试。仅供参考,之前的客户最终通过不同的架构方法解决了这个问题。
      【解决方案3】:

      WS-Security 有不同的标准。在客户端更改绑定可能有意义,因为 basicHttpBinding 和 wsHttpBindings 使用不同的安全标准。

      【讨论】:

      • 谢谢,但该服务仅支持 basicHttpBinding。
      猜你喜欢
      • 2011-10-04
      • 2011-05-09
      • 1970-01-01
      • 2011-02-20
      • 2016-02-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-11
      相关资源
      最近更新 更多