C#/WCF无法实现SOAP UI同款WSDL请求的问题排查
我有一个WSDL,通过SOAP UI可以成功发送请求,需要启用基于WSSE的UsernameToken预认证,预期的SOAP认证头如下:
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> <wsse:UsernameToken wsu:Id="test" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> <wsse:Username>username</wsse:Username> <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">password</wsse:Password> </wsse:UsernameToken> </wsse:Security>
我尝试了多种WCF绑定配置创建客户端,但调用方法时失败:
// 尝试1 BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportWithMessageCredential); binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic; // 尝试2 var bindingws = new WSHttpBinding(SecurityMode.TransportWithMessageCredential); var bindinghttps = new BasicHttpsBinding(BasicHttpsSecurityMode.TransportWithMessageCredential); bindinghttps.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic; // 尝试3 var basicHttpBinding = new BasicHttpBinding(BasicHttpSecurityMode.TransportWithMessageCredential); basicHttpBinding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName; // 客户端初始化 InboundPortClient client = new InboundPortClient(basicHttpBinding, endpointAddress); client.ClientCredentials.UserName.UserName = "your_username"; client.ClientCredentials.UserName.Password = "your_password"; client.Open();// 此步骤正常 client.updateCall(null); // 调用时出错
错误信息:
System.ServiceModel.ProtocolException:
'The content type text/html; charset=utf-8 of the response message does not match the content type of the binding (text/xml; charset=utf-8). If using a custom encoder, be sure that the IsContentTypeSupported method is implemented properly.
The first 1024 bytes of the response were:--access denied page
我认为是绑定配置不正确导致返回访问拒绝页面,请问如何使用WCF/.NET Framework实现与SOAP UI相同的请求?
你混淆了HTTP Basic认证和WSSE UsernameToken认证的核心区别:SOAP UI发送的是消息层面的WSSE认证头(用户名密码嵌入SOAP Header的<wsse:Security>节点),而你尝试的部分配置是在设置HTTP传输层面的Basic Auth,这与服务端要求的认证方式不匹配,导致返回403访问拒绝页面(HTML格式),进而触发内容类型不匹配的错误。
以下是匹配SOAP UI请求的正确WCF配置方案:
1. 针对BasicHttpBinding的正确配置
如果服务端使用的是BasicHttpBinding类型的端点,使用以下代码:
// 安全模式:Transport(HTTPS)+ 消息层面的凭证认证 var binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportWithMessageCredential); // 指定消息凭证类型为UserName,对应WSSE UsernameToken binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName; // 初始化客户端端点 var endpointAddress = new EndpointAddress("https://your-service-endpoint-url"); var client = new InboundPortClient(binding, endpointAddress); // 设置用户名密码 client.ClientCredentials.UserName.UserName = "your_username"; client.ClientCredentials.UserName.Password = "your_password"; // 关键:强制使用明文密码(匹配服务端要求的PasswordText类型) client.ClientCredentials.UserName.PasswordType = System.ServiceModel.Security.PasswordType.Text; try { client.Open(); client.updateCall(null); } finally { // 确保客户端正确关闭 if (client.State != CommunicationState.Closed) client.Close(); }
2. 针对WSHttpBinding的正确配置
如果WSDL中定义的是WSHttpBinding端点,改用以下配置:
var binding = new WSHttpBinding(SecurityMode.TransportWithMessageCredential); // 指定消息凭证类型为UserName binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName; var endpointAddress = new EndpointAddress("https://your-service-endpoint-url"); var client = new InboundPortClient(binding, endpointAddress); client.ClientCredentials.UserName.UserName = "your_username"; client.ClientCredentials.UserName.Password = "your_password"; // 强制明文密码 client.ClientCredentials.UserName.PasswordType = System.ServiceModel.Security.PasswordType.Text; try { client.Open(); client.updateCall(null); } finally { if (client.State != CommunicationState.Closed) client.Close(); }
3. 验证请求一致性(排查问题用)
如果仍无法解决,开启WCF消息日志对比SOAP UI的请求内容,确认SOAP头是否一致:
在项目的App.config或Web.config中添加以下配置:
<system.diagnostics> <sources> <source name="System.ServiceModel.MessageLogging"> <listeners> <add name="messages" type="System.Diagnostics.XmlWriterTraceListener" initializeData="c:\logs\wcf_messages.svclog" /> </listeners> </source> </sources> </system.diagnostics> <system.serviceModel> <diagnostics> <messageLogging logEntireMessage="true" logMalformedMessages="true" logMessagesAtServiceLevel="true" logMessagesAtTransportLevel="true" maxMessagesToLog="3000" maxSizeOfMessageToLog="2000"/> </diagnostics> </system.serviceModel>
生成日志后,查看WCF发送的SOAP请求头是否包含正确的<wsse:Security>节点,重点检查命名空间、密码类型属性是否与SOAP UI的请求一致。
内容的提问来源于stack exchange,提问作者Shivam

