如何配置WCF服务客户端在消息级别同时使用证书和用户名凭据
解决方案
可以实现单条消息同时附加用户名和证书两种消息级身份凭据,需要结合配置修改和少量客户端代码完成,操作步骤如下:
1 修改app.config配置
方案1:沿用basicHttpBinding加自定义端点行为
在原有system.serviceModel节点下添加行为配置,同时修改终结点关联该行为,完整修改后的配置如下:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" /> </startup> <system.serviceModel> <behaviors> <endpointBehaviors> <behavior name="DualCredentialBehavior"> <clientCredentials> <!-- 配置客户端证书,也可以后续在代码中动态指定 --> <clientCertificate findValue="替换为你的证书指纹" storeLocation="CurrentUser" storeName="My" x509FindType="FindByThumbprint"/> </clientCredentials> </behavior> </endpointBehaviors> </behaviors> <bindings> <basicHttpBinding> <binding name="BasicHttpBinding_IEfmUserService" messageEncoding="Mtom"> <security mode="TransportWithMessageCredential"> <message clientCredentialType="Certificate" /> </security> </binding> <binding name="BasicHttpBinding_IEfmUserService1" messageEncoding="Mtom"> <security mode="Transport" /> </binding> </basicHttpBinding> </bindings> <client> <endpoint address="endpointaddress.svc" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IEfmUserService" contract="EfmUserService.IEfmUserService" name="BasicHttpBinding_IEfmUserService" behaviorConfiguration="DualCredentialBehavior"/> </client> </system.serviceModel> </configuration>
方案2:使用自定义绑定(无需额外注入消息头)
如果不想手动构造消息头,可以改用自定义绑定直接支持双重凭据验证:
<bindings> <customBinding> <binding name="DualAuthBinding"> <mtomMessageEncoding /> <security authenticationMode="UserNameForCertificate" messageSecurityVersion="WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10"/> <httpsTransport /> </binding> </customBinding> </bindings>
修改终结点的binding属性为customBinding,bindingConfiguration对应自定义绑定的名称即可。
2 客户端代码配置
如果选用方案1的basicHttpBinding配置,需要在调用服务前手动注入用户名令牌到消息头,示例代码如下:
// 创建服务代理实例 var client = new EfmUserService.IEfmUserServiceClient(); // 若未在配置中指定证书,可通过代码动态设置 // client.ClientCredentials.ClientCertificate.SetCertificate( // StoreLocation.CurrentUser, // StoreName.My, // X509FindType.FindByThumbprint, // "你的证书指纹"); // 注入用户名密码凭据到消息安全头 using (OperationContextScope scope = new OperationContextScope(client.InnerChannel)) { XmlDocument doc = new XmlDocument(); // 构造WSS标准用户名令牌节点 XmlElement usernameToken = doc.CreateElement("wsse", "UsernameToken", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"); XmlElement username = doc.CreateElement("wsse", "Username", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"); username.InnerText = "你的用户名"; XmlElement password = doc.CreateElement("wsse", "Password", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"); password.SetAttribute("Type", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"); password.InnerText = "你的密码"; usernameToken.AppendChild(username); usernameToken.AppendChild(password); // 将令牌加入消息头 MessageHeader securityHeader = MessageHeader.CreateHeader( "Security", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", usernameToken); OperationContext.Current.OutgoingMessageHeaders.Add(securityHeader); // 调用服务方法 var response = client.你的服务方法名(参数); }
注意事项
- 请确保服务端已经提前配置为同时验证消息中的证书和用户名令牌,否则客户端提交的双重凭据会被服务端忽略或拒绝
- 若使用HTTP传输而非HTTPS,建议对密码进行哈希加密后再传递,避免明文泄露
- 证书的指纹输入时要去掉不可见字符,避免匹配失败
内容的提问来源于stack exchange,提问作者Nathan Roberts
相关产品推荐
相关产品推荐

