C#调用WCF服务能否同时使用X509证书与UserName凭据认证
结论
WCF支持单次请求同时携带X509证书和SOAP消息级UserName两种凭据,你当前的问题是绑定配置限制了仅传输证书凭据,导致代码中设置的用户名密码未被发送到服务端。
问题原因
你当前App.config中配置了<message clientCredentialType="Certificate" />,该配置指定消息层仅使用证书作为凭据,WCF会忽略代码中设置的ClientCredentials.UserName属性,不会将其序列化到SOAP消息头中,因此服务端返回缺少凭据的报错。
推荐解决方案:消息检查器手动注入SOAP头(兼容性最高)
该方案无需修改现有绑定配置,可适配任意第三方API的SOAP头格式要求,是对接非WCF实现的服务的首选方案。
1. 实现自定义消息检查器
该类会在请求发送前将用户名密码插入SOAP消息头:
using System.ServiceModel; using System.ServiceModel.Dispatcher; using System.ServiceModel.Channels; public class UserNameHeaderInjector : IClientMessageInspector { private readonly string _userName; private readonly string _password; public UserNameHeaderInjector(string userName, string password) { _userName = userName; _password = password; } public void AfterReceiveReply(ref Message reply, object correlationState) { // 无需处理响应可留空 } public object BeforeSendRequest(ref Message request, IClientChannel channel) { // 请根据服务端要求调整头名称和命名空间 var userNameHeader = MessageHeader.CreateHeader("UserName", "http://服务端要求的命名空间", _userName); var passwordHeader = MessageHeader.CreateHeader("Password", "http://服务端要求的命名空间", _password); request.Headers.Add(userNameHeader); request.Headers.Add(passwordHeader); return null; } }
2. 实现终结点行为注册检查器
using System.ServiceModel.Description; using System.ServiceModel.Dispatcher; public class UserNameHeaderBehavior : IEndpointBehavior { private readonly string _userName; private readonly string _password; public UserNameHeaderBehavior(string userName, string password) { _userName = userName; _password = password; } public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { } public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { clientRuntime.ClientMessageInspectors.Add(new UserNameHeaderInjector(_userName, _password)); } public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { } public void Validate(ServiceEndpoint endpoint) { } }
3. 修改主程序代码注入行为
删除原有设置ClientCredentials.UserName的代码,添加自定义行为即可:
X509Certificate2 cert = new X509Certificate2("path to pfx file", "password for file"); EfmUserServiceClient client = new EfmUserServiceClient(); client.ClientCredentials.ClientCertificate.Certificate = cert; // 新增行:注入自定义头行为 client.Endpoint.EndpointBehaviors.Add(new UserNameHeaderBehavior("test@email.com", "test password")); client.Open(); // 后续原有业务代码无需修改
可选方案:自定义绑定实现标准双凭据
如果服务端遵循WS-Security规范,可直接修改App.config配置,让WCF自动同时传输两种凭据:
<system.serviceModel> <bindings> <customBinding> <binding name="DualCredentialBinding"> <mtomMessageEncoding /> <security authenticationMode="CertificateOverTransport" enableUnsecuredResponse="true"> <issuedTokenParameters /> </security> <httpsTransport /> </binding> </customBinding> </bindings> <client> <endpoint address="实际服务地址" binding="customBinding" bindingConfiguration="DualCredentialBinding" contract="EfmUserService.IEfmUserService" name="BasicHttpBinding_IEfmUserService" /> </client> </system.serviceModel>
该配置下你原有代码中同时设置证书和UserName属性即可正常生效。
内容的提问来源于stack exchange,提问作者Nathan Roberts
相关产品推荐
相关产品推荐

