WCF客户端-如何处理或忽略MustUnderstand标头元素



我正在使用WS-Security编写一个使用非.Net web服务的WCF客户端。服务的响应包含一个安全标头,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客户端错误消息:

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

我的WCF客户端不需要任何时间戳信息。有没有一种简单的方法来插入处理例程?我已经尝试过扩展响应类&正在添加[MessageHeader]属性。

编辑:

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

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

MSDN WCF扩展性

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

这里的设置是基于证书的、Oracle Application Server 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,并为消息检查器添加了一个端点行为。在这一点上,我控制了请求,我可以添加适当的标题并修改Action上的mustUnderstand。

使用SOAPUi,我获取Message.ToString()并将其放入SOAPUi中,然后测试请求。一旦将需要的项目添加到请求中,就确定美洲国家组织服务器没有回复所有必要的内容。使用回复的消息检查器,我修改了消息,使其包含丢失的标头。我不记得在哪里找到了消息检查器的基本代码,但您需要修改代码才能正确地使用它。

对于我的例子,这里有一些片段。

对于中的转换消息

 public object BeforeSendRequest

我需要修改Header,所以使用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。您可能需要修改字符串的值。空的,但这只是一个例子。

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

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

在处理支持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;
        }
    }

最新更新