WCF服务安全控制之netTcpBinding的用户名密码验证

选择netTcpBinding

WCF的绑定方式比较多,常用的大体有四种:

  • wsHttpBinding
  • basicHttpBinding
  • netTcpBinding
  • wsDualHttpBinding

这四种绑定方式中,有两种支持双工通信:

  • wsDualHttpBinding
  • netTcpBinding

在我目前的参与的项目中,考虑到产品面临的环境基本是内网,而且对数据传输效率比较高,可能会有大数据量的传输和频繁的服务访问,并且在项目中涉及到了即时消息模块,需要WCF双工通信支持,我们选用了netTcpBinding的方式。

安全方案

一般而言内网部署采用netTcpBinding方式,外网则采用能免受防火墙阻碍的wsHttpBinding绑定,由于内网中的服务相对是一个安全的环境,所以WCF的配置多数采用的是windows验证方式

<message clientCredentialType="Windows"/>

当然也可以使用证书、用户名密码等方式来控制安全,很早就想将项目中的安全控制做一下,也查阅了很多资料,但发现netTcpBinding绑定的安全控制方面的资料比较少,很多都是讲wsHttpBinding和basicHttpBinding的,在用户名密码验证方面,目前为止发现两种可行的安全控制方案:

  1. 改写SOAP的Header,用来传递用户名和密码,但由于是明文传递,并不安全
  2. 利用WCF自身的安全机制,重写验证类来实现验证

第一种方案在客户端给SOAP加上标识时需要有一个执行的范围限制,不太好抽象出来做成Proxy的工厂类:

var proxy = new Service.Service1Client();
            string result = string.Empty;
            using (OperationContextScope scope = 
new OperationContextScope(proxy.InnerChannel))
            {
                MessageHeader header = MessageHeader.CreateHeader("myname", "myname_ns",
 "myname_value");
                OperationContext.Current.OutgoingMessageHeaders.Add(header);
                result = proxy.GetData(11);
            } 

于是想摸索一下WCF内在的安全机制,发现在netTcpBinding绑定模式下采用用户名密码方式来验证,需要对用户名

和密码进行加密,WCF认为在传递SOAP时应该对用户名密码这些敏感信息采用加密方案,这个方案选择了X509认证的方式,在MSDN上有对X509的详细说明,这里不再赘述。

这里我们通过一个Demo来了解一下整个过程

实例验证

首先我们有一个服务

[ServiceContract]
    public interface IAddService
    {
        [OperationContract]
        string Login(string name);
    }
    
    public class AddService:IAddService
    { 
        public string Login(string name)
        {
            if (OperationContext.Current.ServiceSecurityContext.PrimaryIdentity.IsAuthenticated)
            {
                name += "OK....";
            }
            return name;
        }     
    } 
大家注意到这个服务的Login方法中有一个判断这个判断就是WCF内部对客户端传递username和password验证的过程。
我们通过继承System.IdentityModel.Selectors.UserNamePasswordValidator 来重写Validate方法,
自定义用户名和
密码验证。
public class Validator : System.IdentityModel.Selectors.UserNamePasswordValidator
    {
        public override void Validate(string userName, string password)
        {
            if (userName == "wengyuli" && password == "pwd")
            {

            }
        }
    }
服务的配置文件,首先是个BindingConfig节点
<bindings> 
      <netTcpBinding>
        <binding name="netTcpBindConfig">
          <security mode="Message">
            <message clientCredentialType="UserName"  />
          </security>
        </binding>
      </netTcpBinding>
    </bindings>
Service段得配置也比较普通
<service behaviorConfiguration="MyBehavior" name="HostTcpTest.AddService">
        <host>
          <baseAddresses>
            <add baseAddress="net.tcp://localhost:4507/AddService"/>
          </baseAddresses>
        </host>
        <endpoint address="" binding="netTcpBinding" contract="HostTcpTest.IAddService" b
indingConfiguration="netTcpBindConfig"></endpoint>
        <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"></endpoint>
      </service>
另外比较重要的是服务的行为配置,这里面可以控制植物或天气等等。
<behavior name="MyBehavior" >
          <serviceMetadata/>
          <serviceDebug includeExceptionDetailInFaults="true" />
          <dataContractSerializer maxItemsInObjectGraph="6553600"/>
          <serviceCredentials>
            <serviceCertificate storeName="My" findValue="MyServer" 
x509FindType="FindBySubjectName" storeLocation="CurrentUser"/>
            <clientCertificate>
              <!--自á?定?§义°?对?客¨a户?ì端?进?行D证?è书o¨|认¨?证?è方¤?式o? 这a里¤?为a None-->
              <authentication certificateValidationMode="None" />
            </clientCertificate>
            <userNameAuthentication userNamePasswordValidationMode="Custom"
             customUserNamePasswordValidatorType="HostTcpTest.Validator,HostTcpTest"/>
          </serviceCredentials>
        </behavior>
当你做完这些后,注意上面的代码中有数字证书的一部分,这个数字证书是这样生成的:
makecert -sr localmachine -ss My -n CN=MyServer-sky exchange -pe –r
 
在Clleit引用一下服务,生成代理类后就可以在客户端编写访问服务的代码了:
var proxy = new AddService.AddServiceClient();
            proxy.ClientCredentials.UserName.Password = "asd123,.";
            proxy.ClientCredentials.UserName.UserName = "wengyuli";
            Console.WriteLine(proxy.Login("23"));
让我们启动一下项目试试,注意VS要以管理员身份启动。
我们会发现有一个错误,服务协商有问题,于是我重新做了一遍配置,终于可以了,现在分享出来:
当我们使用上面的命令时:
image
当将这个MyServer复制到’受限制的根证书颁发机构’时,即可解决问题。
image
 
image
 

后续

对于x509认证,笔者目前也没有深入了解,打算在深入了解后写一篇博文。
 
附上DEMO下载:http://files.cnblogs.com/wengyuli/TcpTest.rar

posted @ 2011-05-14 00:02 翁玉礼 阅读(...) 评论(...) 编辑 收藏