我正在编写一个WCF
客户端,它使用WS-Security来使用非.Net Web服务。服务的响应包含一个Security头,其mustUnderstand设置为true。
使用ServiceModelListener,我确实看到从服务返回的实际数据。但是,WCF客户端失败,因为它没有处理安全性标头。
<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”中的标题“安全性”,导致邮件无法处理。此错误通常表示此消息的发送方已启用接收方无法处理的通信协议。请确保客户端绑定的配置与服务的绑定一致。
我的WCF
客户端不需要任何时间戳信息。是否有一种简单的方法来处理例程?我已经尝试过扩展Response类&amp;添加[MessageHeader]属性。
修改
问另一种方法:如何实现接受标记为必须理解的自定义标题元素的
WCF
客户端?
答案 0 :(得分:3)
我遇到了类似的问题。我不确定这是否有用。
MSDN WCF可扩展性
http://blogs.msdn.com/b/carlosfigueira/archive/2011/04/19/wcf-extensibility-message-inspectors.aspx
此处的设置是基于证书,Oracle应用服务器10g和.Net来使用服务。在尝试弄清楚Request和然后响应时发生了什么时,使用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服务器没有回复所有必需的元素。使用消息检查器进行回复我修改了消息以包含缺少的标头。我不记得在哪里找到了消息检查器的基本代码,但您需要修改代码才能正确使用它。
我的例子中有一些片段。
对于
中的转换消息 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);
我还必须修改动作标题
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中。
答案 1 :(得分:1)
WS-Security有不同的标准。可能有意义的是在客户端更改绑定,因为basicHttpBinding和wsHttpBindings正在使用不同的安全标准。
答案 2 :(得分:0)
遇到一个问题,涉及支持ONVIF的IP摄像机的一些代码。摄像机发回了Nonce并在“安全性”元素中创建,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;
}
}