【WCF--初入江湖】11 安全

11 安全

前言

  【1】传输安全
     传输安全模式
     传输安全与绑定协议
 
  【2】身份验证
     身份验证分类
     证书
     示例:传输安全匿名客户端证书的使用

 

1. 传输安全

   

     保证信息在传输过程中的安全.传输安全是身份验证和授权的前提。

  传输安全模式

    None:关闭了传输安全的功能
    transport security:对通道上所有的通信加密,除了接收者外,没有任何一方可以看见消息的内容。
    message security:只是对消息的本身进行加密而不是对传输进行加密。
    Mixed:在实现完整性和机密性以及服务端身份验证时采用的是Transport Security模式,而在保护客户端安全凭证的安全时采用了Message Security模式。
    Both:同是使用Transport模式和Message模式,就是传输时安全的,传输的消息也是经过加密的.

 

      配置

    传输安全模式是在绑定中配置的.

 

  binding与传输安全模式

    传输安全模式与各种binding是一个组合的关系,并不是每种binding都能应用所有的传输安全模式,

  所有WCF的绑定默认都是安全的,只有BasicHttpBinding默认是非安全的

名称

None

Transport

Message

Mixed

Both

BasicHttpBinding

Yes(默认)

Yes

Yes

Yes

No

NetTcpBinding

Yes

Yes(默认)

Yes

Yes

No

NetNamedPipeBinding

Yes

Yes(默认)

No

No

No

WSHttpBinding

Yes

Yes

Yes(默认)

Yes

No

WSDualHttpBinding

Yes

No

Yes(默认)

No

No

NetMsmqBinding

Yes

Yes(默认)

Yes

No

Yes

     配置文件的形式:

<bindings>
  <netTcpBinding>
    <binding name="tcpBinding">
      <security mode="Transport">
        <transport clientCredentialType="Windows“ 
    protectionLevel="EncryptAndSign"/>
      </security>
    </binding>
  </netTcpBinding>
</bindings>

  编程方式实现:

NetTcpBinding binding = new NetTcpBinding(SecurityMode.Transport);

  或者

NetTcpBinding binding1 = new NetTcpBinding();
binding1.Security.Mode = SecurityMode.Transport;


 

2. 身份验证

 

2.1 Transport模式的身份验证

 

  在Transport模式下,身份验证与binding的关系

    

 

名称

None

Windows

UserName

Certificate

BasicHttpBinding

Yes(默认)

Yes

Yes

Yes

NetTcpBinding

Yes

Yes(默认)

No

Yes

NetNamedPipeBinding

No

Yes(默认)

No

No

WSHttpBinding

Yes

Yes(默认)

Yes

Yes

WSDualHttpBinding

 

 

 

 

NetMsmqBinding

Yes

Yes(默认)

No

Yes

    

    所有的局域网绑定都支持Windows方式的身份验证

  NetTcpBinding在Transport模式下不支持UserName身份验证模式

  wsDualHttpBinding不支持Transport传输安全模式的

 

 2.2 Message模式的身份验证

     在Message传输安全模式下Binding与身份验证

 

 

名称

None

Windows

UserName

Certificate

IssuedToken

BasicHttpBinding

No

No

No

Yes

No

NetTcpBinding

Yes

Yes(默认)

Yes

Yes

Yes

NetNamedPipeBinding

 

 

 

 

 

WSHttpBinding

Yes

Yes(默认)

Yes

Yes

Yes

WSDualHttpBinding

Yes

Yes(默认)

Yes

Yes

Yes

NetMsmqBinding

Yes

Yes(默认)

Yes

Yes

Yes

 

    除了BasicHttpBinding和NetNamedPipeBinding以外,其他的模式默认都是用Windows凭证

 

    示例:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>

  <system.web>
    <compilation debug="true" />
  </system.web>
  <system.serviceModel>
    <bindings>
      <wsHttpBinding>
        <binding name="wsHttpBinding_Security">
          <security mode="Transport">
            <transport clientCredentialType="None"/><!--启用传输安全,身份认证是匿名的-->
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>
    <services>
      <service name="Keasy5.WCF.Security.Translation.Service1">
        <host>
          <baseAddresses>
            <!--Http和SSL 两者要成对-->
            <add baseAddress = "http://localhost:8733/Design_Time_Addresses/Keasy5.WCF.Security.Translation/Security/" />
            <add baseAddress = "https://localhost:8833/Design_Time_Addresses/Keasy5.WCF.Security.Translation/Security/" />
          </baseAddresses>
        </host>
        <endpoint address="" 
                  binding="wsHttpBinding" 
                  bindingConfiguration="wsHttpBinding_Security"
                  contract="Keasy5.WCF.Security.Translation.IService1">
          <identity>
            <dns value="localhost"/>
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpGetEnabled="True"/>
          <serviceDebug includeExceptionDetailInFaults="False" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>

</configuration>
View Code

 

 其中:

    <bindings>
      <wsHttpBinding>
        <binding name="wsHttpBinding_Security">
          <security mode="Transport">
            <transport clientCredentialType="None"/><!--启用传输安全,身份认证是匿名的-->
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>
    <services>
      <service name="Keasy5.WCF.Security.Translation.Service1">
        <host>
          <baseAddresses>
            <add baseAddress = "http://localhost:8733/Design_Time_Addresses/Keasy5.WCF.Security.Translation/Security/" />
            <add baseAddress = "https://localhost:8833/Design_Time_Addresses/Keasy5.WCF.Security.Translation/Security/" />
          </baseAddresses>
        </host>
        <endpoint address="" 
                  binding="wsHttpBinding" 
                  bindingConfiguration="wsHttpBinding_Security"
                  contract="Keasy5.WCF.Security.Translation.IService1">

    注意:如果启用了传输安全,即:<security mode="Transport">,那么就必须提供:https开头的基地址

            <add baseAddress = "http://localhost:8733/Design_Time_Addresses/Keasy5.WCF.Security.Translation/Security/" />
            <add baseAddress = "https://localhost:8833/Design_Time_Addresses/Keasy5.WCF.Security.Translation/Security/" />

  否则WCF服务抛出异常:

  下面是我们注释掉:https:基地址后,

 

        <baseAddresses>
            <add baseAddress = "http://localhost:8733/Design_Time_Addresses/Keasy5.WCF.Security.Translation/Security/" />
            <!--<add baseAddress = "https://localhost:8833/Design_Time_Addresses/Keasy5.WCF.Security.Translation/Security/" />-->
          </baseAddresses>

  抛出异常

System.InvalidOperationException: 找不到具有绑定 WSHttpBinding 的终结点的与方案 https 匹配的基址。注册的基址方案是 [http]。

 

    设置完上述的配置后,客户端(这里使用VS对WCF服务库内置的【WCF测试客户端】)调用时发生异常:

对 https://localhost:8833/Design_Time_Addresses/Keasy5.WCF.Security.Translation/Security/ 执行 HTTP 请求时发生错误。这可能是由于在使用 HTTPS 的情况下未使用 HTTP.SYS 正确配置服务器证书造成的。这还可能是由于客户端和服务器之间的安全绑定不匹配所致。

  这是因为配置中,我们启用了传输安全,但没有提供证书。

<security mode="Transport">

          启用了传输安全,就必须使用证书。如何提供证书,请看下一节:3. 证书与传输安全。

    

3. 证书与传输安全

 

   3.1 传输安全必须要使用证书

    SSL安全套接字层是一个数据传输加密机制,它可以确保在客户机与服务器之间传输的数据仍然是安全与隐密的。
    传输安全 (HTTPS) 确保保密性和完整性。
    SSL 广泛用于 Internet 中,以便向客户端证明服务的身份,并且随后向通道提供保密性(加密)。
    证书就相当于是身份证一样,客户端可以不进行身份验证。但是要有一个服务器证书来保证客户端和服务器之间能够建立SSL安全套接字连接。
 

      3.2 证书制作

 

    【1】使用Makecert命令创建证书
      Makecert证书创建工具生成的 X.509 证书。
    它创建用于数字签名的公钥和私钥对,并将其存储在证书文件中。
    此工具还将密钥对与指定发行者的名称相关联,并创建一个 X.509 证书,该证书将用户指定的名称绑定到密钥对的公共部分。

     步骤:

    第一步:创建证书

      打开Vs的【VS2013 开发人员命令提示】,输入如下命令

makecert -sr localmachine -ss My -n CN=WCFServerPK -sky exchange -pe -r

       其中,

    -sr localmachine     -sr指定证书的存放位置,localmachine 指是存储在本机上 ,也可以是current,存放到当前用户 
     -ss My               -ss指定证书存储的目录,My指【个人】这个目录,如果用【Hello】,则创建一个【Hello】目录
    -n CN=WCFServerPK     -n 指定证书的名称,cn=前缀(Common Name),WCFServerPK 是证书的名称

  第二步:查看证书
    打开【命令】(或者Win+R),输入mmc,确定


  再选【计算机账户(C)】选项,添加控制台节点。

 

  第三步:导出证书:

    右键证书--所有任务--导出,在导出过程中有一步是要求输入密码(这里设置为123456),然后输入证书的名称WCFServicePK,最后导出

      WCFServicePK.pfx文件。

  

  第四步:在客户端上安装证书。

    在客户端所在的计算机上,安装在第一步中打开的【控制台】,在【证书(本地计算机)】--【受信任人】--【证书】右键--【所有任务】--

  【导入】,把第三步导出的证书导入,下面的过程证有一步要求输入证书密码(改密码就是第三步设置的证书密码123456)。

    也可以为其他地方(如:受信任的根证书颁发机构)导入证书WCFServicePK.pfx。

   

  第五步:设置SSL证书

      需要一个工具:httpcfg.下载地址:

          链接: http://pan.baidu.com/s/1c04DMSg 密码: me2p

          下载该工具后,把它解压并保存到一个目录下,本例使用:D:/

           打开命名(win+R),输入cmd,切换到D:目录

           查看SSL配置

httpcfg query ssl

       设置SSL配置

      在第二步中打开的【控制台】界面的右边列表中,选择WCFServicePK,双击打开证书(或者右键--打开),在弹出框中,

    选择【详细信息】--选择【指纹】,复制这些字符串,本例中这些字符串为:

           832420b5499bf8eca2c924e63fb9b6192201d034

        然后在输入如下命令:

httpcfg set ssl -i 0.0.0.0:8833 -h 832420b5499bf8eca2c924e63fb9b6192201d034

 

        这样,使得证书和端口8833进行关联,其中:  

    【1】832420b5499bf8eca2c924e63fb9b6192201d034 就是证书的指纹。

         【2】0.0.0.0:8833          8833是端口,这就是配置WCF服务时指定的Https基地址的端口

<add baseAddress = "https://localhost:8833/Design_Time_Addresses/Keasy5.WCF.Security.Translation/Security/" />

 

  如果要删除SSL配置,可以输入:

httpcfg delete ssl -i 0.0.0.0:8833 -h 832420b5499bf8eca2c924e63fb9b6192201d034

     

  第六步:服务端配置证书:

    <services>
      <service 
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpGetEnabled="True"/>
          <serviceDebug includeExceptionDetailInFaults="False" />

          <serviceCredentials>
            <serviceCertificate storeLocation="LocalMachine"
                                x509FindType="FindBySubjectName"
                                findValue="WCFServerPK" 
                                storeName="My"/>
          </serviceCredentials>
          
        </behavior>
      </serviceBehaviors>
    </behaviors>

 

      其中,findValue的值是:3.2 第一步中 “-n CN=WCFServerPK"中指定的值

      这里新建一个控制台程序作为宿主:

      宿主的配置app.config:

  

<?xml version="1.0" encoding="utf-8" ?>
<configuration>

  <system.web>
    <compilation debug="true" />
  </system.web>
  <system.serviceModel>
    <bindings>
      <wsHttpBinding>
        <binding name="wsHttpBinding_Security">
          <security mode="Transport">
            <transport clientCredentialType="None"/><!--启用传输安全,身份认证是匿名的-->
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>
    <services>
      <service name="Keasy5.WCF.Security.Translation.Service1">
        <host>
          <baseAddresses>
            <add baseAddress = "http://localhost:8733/Keasy5.WCF.Security.Translation/Security/" />
            <add baseAddress = "https://localhost:8833/Keasy5.WCF.Security.Translation/Security/" />
          </baseAddresses>
        </host>
        <endpoint address="" 
                  binding="wsHttpBinding" 
                  bindingConfiguration="wsHttpBinding_Security"
                  contract="Keasy5.WCF.Security.Translation.IService1">
          <identity>
            <dns value="localhost"/>
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpGetEnabled="True"/>
          <serviceDebug includeExceptionDetailInFaults="False" />

          <serviceCredentials>
            <serviceCertificate storeLocation="LocalMachine"
                                x509FindType="FindBySubjectName"
                                findValue="WCFServerPK" 
                                storeName="My"/>
          </serviceCredentials>
          
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>

</configuration>
View Code

  宿主代码:

namespace Keasy5.WCF.Security.Translation.Host
{
    class Program
    {
        static void Main(string[] args)
        {
            using (ServiceHost serviceHost = new ServiceHost(typeof(Service1)))
            {
                serviceHost.Open();
                Console.WriteLine("服务已启动");
                Console.ReadKey();
            }
        }
    }
}

 

       第七步:客户端调用服务:

    新建一个控制台项目,然后引用服务,输入如下地址:

    https://localhost:8833/Keasy5.WCF.Security.Translation/Security/

    然后点击【转到】,这时候,弹出一个对话框【安全报警,说明SSL证书配置成功。

    客户端自动生成的app.cong配置:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <bindings>
            <wsHttpBinding>
                <binding name="WSHttpBinding_IService1">
                    <security mode="Transport">
                        <transport clientCredentialType="None" />
                    </security>
                </binding>
            </wsHttpBinding>
        </bindings>
        <client>
            <endpoint address="https://localhost:8833/Keasy5.WCF.Security.Translation/Security/"
                binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IService1"
                contract="ServiceReference1.IService1" name="WSHttpBinding_IService1">
                <identity>
                    <dns value="localhost" />
                </identity>
            </endpoint>
        </client>
    </system.serviceModel>
</configuration>

  

   调用服务方法:

namespace Keasy5.WCF.Security.Translation.Client
{
    class Program
    {
        static void Main(string[] args)
        {
            Service1Client  service1Client = new Service1Client();

            try
            {
                string message = service1Client.GetData("Https传输安全");
                Console.WriteLine(message);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }

            Console.ReadKey();

        }
    }
}

  先启动宿主,后启动客户端;

      这时,客户端抛出如下异常:

无法为 SSL/TLS 安全通道与颁发机构“localhost:8833”建立信任关系。

     客户端调用还需要添加代码,强制信任证书:

namespace Keasy5.WCF.Security.Translation.Client
{
    class Program
    {
        static void Main(string[] args)
        {
            Service1Client  service1Client = new Service1Client();

            //强制信任证书
            ServicePointManager.ServerCertificateValidationCallback +=ServerCertificateValidationCallback;  
try { string message = service1Client.GetData("Https传输安全"); Console.WriteLine(message); } catch (Exception e) { Console.WriteLine(e.Message); } Console.ReadKey(); } private static bool ServerCertificateValidationCallback( object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { return true; } } }

 

 

 源码下载

 链接: http://pan.baidu.com/s/1eQh9iq2 密码: 4ogd

    

 

  案例总结

    WSHttpBinding 绑定,匿名客户端(none)的传输安全模式(Transport)

       1、制作证书

       2、SSL证书设置

       3、服务端配置

       4、客户端配置

   

5. 身份认证

  【1】身份验证类型(transport客户端凭证类型)

    1.None
    2.Basic
    3.Digest
    4.HTLM
    5.Windows
    6.Certificate
    
<bindings>
      <netTcpBinding>
        <binding name="">
          <security mode="Transport">
            <transport clientCredentialType="Windows"/>
          </security>
        </binding>
      </netTcpBinding>
    </bindings>


  【2】身份验证类型(Message客户端凭证类型)

    1.None
    2.Windows
    3.Username
    4.Certificate
    5.Windows CardSpace

  

 <bindings>
      <wsHttpBinding>
        <binding name="">
          <security mode="Message">
            <message clientCredentialType="UserName"/>
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>

 

5.1 示例:Basic身份验证

    传输安全模式之基本身份验证:

    传输安全模式之基本身份验证需要服务器需要一个有效的可用于安全套接字层 (SSL) 的 X.509 证书,

  并且客户端必须信任此服务器证书。如果不信任此证书会导致建立SSL传输连接失败,因为客户端会认为服务端是一个非法的服务端,因而建立SSL安全套接层失败。

    1.身份验证(服务器):提供证书,(使用 HTTPS)
    2.身份验证(客户端):提供用户名/密码

 

    客户端建立SSL安全套接层以后,会使用协定的密码对消息签名,客户端使用证书加密数据,服务端使用证书解密数据,保证数据的安全和机密性。

    步骤:

        1、制作证书

        2、SSL证书设置

        3、服务端配置

                  传输模式设置

                         证书使用

        4、客户端配置(用户名和密码)

  第一步--第二步:与上一节相同。

  第三步:服务端的配置:

    

<?xml version="1.0" encoding="utf-8" ?>
<configuration>

  <system.web>
    <compilation debug="true" />
  </system.web>
  <system.serviceModel>
    <bindings>
      <wsHttpBinding>
        <binding name="wsHttpBinding_Transport">
          <security mode="Transport">
            <transport clientCredentialType="Basic"/><!--Basics是用户名和密码的身份认证模式-->
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>
    <services>
      <service name="Keasy5.WCF.Credential.Service.Service1">
        <host>
          <baseAddresses>
            <add baseAddress = "https://localhost:8833/Keasy5.WCF.Credential.Service/Service1/" />
          </baseAddresses>
        </host>
        <endpoint address="" 
                  binding="wsHttpBinding" 
                  bindingConfiguration="wsHttpBinding_Transport"
                  contract="Keasy5.WCF.Credential.Service.IService1">
          <identity>
            <dns value="localhost"/>
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexHttpsBinding" contract="IMetadataExchange"/>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpsGetEnabled="True"/>
          <serviceDebug includeExceptionDetailInFaults="False" />
          <serviceCredentials>
            <serviceCertificate storeLocation="LocalMachine"
                                storeName="My"
                                x509FindType="FindBySubjectName"
                                findValue="WCFServerPK"/>
          </serviceCredentials>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>

</configuration>

 

   第四步:客户端调用服务

    新建客户端控制台项目,添加服务引用,输入如下地址:

https://localhost:8833/Keasy5.WCF.Credential.Service/Service1/

  点击【转到】,弹出一个对话框,要求你输入账户和密码:

  输入服务器主机的密码后,成功添加服务器引用。

      app.config文件自动添加WCF客户端配置:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <bindings>
            <wsHttpBinding>
                <binding name="WSHttpBinding_IService1">
                    <security mode="Transport">
                        <transport clientCredentialType="Basic" />
                    </security>
                </binding>
            </wsHttpBinding>
        </bindings>
        <client>
            <endpoint address="https://localhost:8833/Keasy5.WCF.Credential.Service/Service1/"
                binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IService1"
                contract="ServiceReference1.IService1" name="WSHttpBinding_IService1">
                <identity>
                    <dns value="localhost" />
                </identity>
            </endpoint>
        </client>
    </system.serviceModel>
</configuration>

  添加客户端调用代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using Keasy5.WCF.Credential.Client.ServiceReference1;

namespace Keasy5.WCF.Credential.Client
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                ServiceReference1.Service1Client service1 = new Service1Client();
                ServicePointManager.ServerCertificateValidationCallback += ServerCertificateValidationCallback;

                string message = service1.GetData(200);

                Console.WriteLine(message);
                
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }

            Console.ReadKey();
        }

        private static bool ServerCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
        {
            return true; //强制信任证书
        }
    }
}

 

  如果没有添加如下代码,提供服务器端主机的用户名和密码:

                //使用用户名和密码进行登录服务器
                service1.ClientCredentials.UserName.UserName = "easy5";
                service1.ClientCredentials.UserName.Password = " ";

 

  就会抛出异常:

未提供用户名。请在 ClientCredential 中指定用户名。

    更正客户端代码:

namespace Keasy5.WCF.Credential.Client
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                ServiceReference1.Service1Client service1 = new Service1Client();

                //强制信任证书
                ServicePointManager.ServerCertificateValidationCallback += ServerCertificateValidationCallback;

                //使用用户名和密码进行登录服务器
                service1.ClientCredentials.UserName.UserName = "easy5";
                service1.ClientCredentials.UserName.Password = " ";

                string message = service1.GetData(200);

                Console.WriteLine(message);
                
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }

            Console.ReadKey();
        }

        private static bool ServerCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
        {
            return true; //强制信任证书
        }
    }
}

  

 本节源码下载:

链接: http://pan.baidu.com/s/1hqtauMw 密码: eh0d

 

5.2 示例:Basic身份验证之自定义身份验证

 

    5.1 示例:Basic身份验证 中的身份验证需要客户端知道服务器端的密码,显然这很不合理,

  

  所以自定义一种验证方式。

   【1】自定义身份验证配置如下:

         <serviceCredentials>
        。。。。。。
<clientCertificate> <authentication certificateValidationMode="None"/> </clientCertificate> <userNameAuthentication userNamePasswordValidationMode=“Custom”
                      customUserNamePasswordValidatorType=“命名空间名.自定义验证类名,程序集名"/> </serviceCredentials>

  注意:

             customUserNamePasswordValidatorType=“命名空间名.自定义验证类名, 程序集名"

      必须是:命名空间名.自定义验证类名, 程序集名

      否则抛出异常:

未能从程序集“System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089”中
加载类型“Keasy5.WCF.Credential.Service.MyCustomUserNamePasswordValidator”。":"Keasy5.WCF.Credential.Service.MyCustomUserNamePasswordValidator"}

 

  【2】自定义验证类

class CustomValidator : UserNamePasswordValidator
    {
        public override void Validate(string userName, string password)
        {
            if (userName != "xxx" || password != "xxx123")
            {
                Console.WriteLine("用户名或密码出错!");
                throw new FaultException("用户名或密码出错!");
            }
    }

       实现步骤:

    在5.1 示例:Basic身份验证示例的基础上进行修改

   第一步:在WCF服务项目:

      添加System.IdentityModel程序集的引用,

      添加一个j继承自UserNamePasswordValidator的类MyCustomUserNamePasswordValidator:

using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.IdentityModel;
using System.IdentityModel.Tokens;
using System.IdentityModel.Selectors;

namespace Keasy5.WCF.Credential.Service
{
    public class MyCustomUserNamePasswordValidator : UserNamePasswordValidator
    {
        public override void Validate(string userName, string password)
        {
            //模拟数据库中的账号和密码
            if (userName.ToLower().Trim() != "admin"
                && password.ToLower().Trim() != "admin")
            {
                throw new FaultException("用户名或密码错误");
            }
        }
    }
}

  第二步:在服务的配置文件app.config中的<serviceCredentials>节点下添加:

 

            <!--添加自定义身份验证-->
            <clientCertificate>
              <authentication certificateValidationMode="None"/>
            </clientCertificate>
            <userNameAuthentication userNamePasswordValidationMode="Custom"
                                    customUserNamePasswordValidatorType="Keasy5.WCF.Credential.Service.MyCustomUserNamePasswordValidator
,Keasy5.WCF.Credential.Service
"/>

 

      最终的服务端配置文件为:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>

  <system.web>
    <compilation debug="true" />
  </system.web>
  <system.serviceModel>
    <bindings>
      <wsHttpBinding>
        <binding name="wsHttpBinding_Transport">
          <security mode="Transport">
            <transport clientCredentialType="Basic"/><!--Basics是用户名和密码的身份认证模式-->
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>
    <services>
      <service name="Keasy5.WCF.Credential.Service.Service1">
        <host>
          <baseAddresses>
            <add baseAddress = "https://localhost:8833/Keasy5.WCF.Credential.Service/Service1/"/>
          </baseAddresses>
        </host>
        <endpoint address="" 
                  binding="wsHttpBinding" 
                  bindingConfiguration="wsHttpBinding_Transport"
                  contract="Keasy5.WCF.Credential.Service.IService1">
          <identity>
            <dns value="localhost"/>
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexHttpsBinding" contract="IMetadataExchange"/>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpsGetEnabled="True"/>
          <serviceDebug includeExceptionDetailInFaults="False" />
          <serviceCredentials>
            <serviceCertificate storeLocation="LocalMachine"
                                storeName="My"
                                x509FindType="FindBySubjectName"
                                findValue="WCFServerPK"/>
            
            <!--添加自定义身份验证-->
            <clientCertificate>
              <authentication certificateValidationMode="None"/>
            </clientCertificate>
            <userNameAuthentication userNamePasswordValidationMode="Custom"
                                    customUserNamePasswordValidatorType="Keasy5.WCF.Credential.Service.MyCustomUserNamePasswordValidator,Keasy5.WCF.Credential.Service"/>
            
          </serviceCredentials>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>

</configuration>
View Code

      服务宿主:

using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using Keasy5.WCF.Credential.Service;

namespace Keasy5.WCF.Credential.Host
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                using (ServiceHost serviceHost = new ServiceHost(typeof(Service1)))
                {
                    serviceHost.Open();
                    Console.WriteLine("服务已经启动,按任意键关闭服务!");
                    Console.ReadKey();
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                Console.ReadKey();
            }

            Console.ReadKey();
        }
    }
}

    第三步:客户端引用服务:

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using Keasy5.WCF.Credential.Client.ServiceReference1;

namespace Keasy5.WCF.Credential.Client
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                ServiceReference1.Service1Client service1 = new Service1Client("WSHttpBinding_IService1");

                //强制信任证书
                ServicePointManager.ServerCertificateValidationCallback += 
                    ServerCertificateValidationCallback;

                //使用用户名和密码进行登录服务器
                service1.ClientCredentials.UserName.UserName = "admin";
                service1.ClientCredentials.UserName.Password = "admin";

                string message = service1.GetData(200);

                Console.WriteLine(message);
                
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }

            Console.ReadKey();
        }

        private static bool ServerCertificateValidationCallback(
            object sender, 
            X509Certificate certificate, 
            X509Chain chain, 
            SslPolicyErrors sslPolicyErrors)
        {
            return true; //强制信任证书
        }
    }
}

 

     客户端的配置app.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <bindings>
            <wsHttpBinding>
                <binding name="WSHttpBinding_IService1">
                    <security mode="Transport">
                        <transport clientCredentialType="Basic" />
                    </security>
                </binding>
            </wsHttpBinding>
        </bindings>
        <client>
            <endpoint address="https://localhost:8833/Keasy5.WCF.Credential.Service/Service1/"
                binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IService1"
                contract="ServiceReference1.IService1" 
                      name="WSHttpBinding_IService1">
                <identity>
                    <dns value="localhost" />
                </identity>
            </endpoint>
        </client>
    </system.serviceModel>
</configuration>


 

  本节源码下载:

  链接: http://pan.baidu.com/s/18fG9g 密码: 5t2g

 

 

 

【The End】

 

 

 

posted @ 2014-07-05 04:49  easy5  阅读(2613)  评论(0编辑  收藏  举报