WCF简单教程(8) 安全 - Windows认证

第八篇:WCF安全

WCF提供了非常丰富的加密机制与审核机制,以保证对外提供的服务安全可靠。本文是简单教程,所以只挑其中的一小部分来聊聊。

先来看看最简单的Windows认证。

所谓Windows认证,是指客户端访问时,要提供服务端认可的Windows用户身份。

1、服务端

安全配置主要体现在App.config中:

   
   
   
   
  1. <?xmlversion="1.0"encoding="utf-8"?>

  2. <configuration>

  3. <system.serviceModel>

  4. <services>

  5. <servicename="Server.DataProvider">

  6.        <!-- 本例中使用netTcpBinding,注意指定了一个名为tcpBinding的设置,见下文 -->

  7. <endpointaddress=""binding="netTcpBinding"contract="Server.IData"bindingConfiguration="tcpBinding"/>

  8. <host>

  9. <baseAddresses>

  10. <addbaseAddress="net.tcp://localhost:8081/wcf"/>

  11. </baseAddresses>

  12. </host>

  13. </service>

  14. </services>

  15. <!--客户端的身份认证是设置在bindings节中的-->

  16. <bindings>

  17.      <!--注意此节要与前面的binding匹配,表示为netTcpBinding方式的绑定进行配置-->

  18. <netTcpBinding>

  19.        <!--定义一个名为tcpBinding的设置,就是前面endpoint中用到的-->

  20. <bindingname="tcpBinding">

  21.          <!--使用Message方式的加密,至于它与Transport方式的区别,可先忽略,后面再讲-->

  22. <securitymode="Message">

  23.            <!--指定客户端认证使用Windows方式-->

  24. <messageclientCredentialType="Windows"/>

  25. </security>

  26. </binding>

  27. </netTcpBinding>

  28. </bindings>

  29. </system.serviceModel>

  30. </configuration>

契约实现类我们修改一下,方便看到调用者的身份:

   
   
   
   
  1. using System;

  2. using System.ServiceModel;

  3. namespace Server

  4. {

  5.    [ServiceBehavior]

  6. publicclass DataProvider : IData

  7.    {

  8. publicstring SayHello()

  9.        {

  10.            //WindowsIdentity即代表访问者的身份标识

  11. returnstring.Format("Hello {0}", OperationContext.Current.ServiceSecurityContext.WindowsIdentity.Name);

  12.        }

  13.    }

  14. }


2、客户端

客户端要对应调整App.config:

   
   
   
   
  1. <?xmlversion="1.0"encoding="utf-8"?>

  2. <configuration>

  3. <system.serviceModel>

  4. <client>

  5.      <!--定义endpoint时指定使用特定的配置,另外这个IP是一会儿运行服务端的机器的IP-->

  6. <endpointbinding="netTcpBinding"contract="Server.IData"address="net.tcp://192.168.90.90:8081/wcf"name="DataProvider"bindingConfiguration="tcp"/>

  7. </client>

  8.    <!--客户端与服务端的bindings节设置一致-->

  9. <bindings>

  10. <netTcpBinding>

  11. <bindingname="tcp">

  12. <securitymode="Message">

  13. <messageclientCredentialType="Windows"/>

  14. </security>

  15. </binding>

  16. </netTcpBinding>

  17. </bindings>

  18. </system.serviceModel>

  19. </configuration>


然后是代码,要指定访问时客户端使用的用户名密码:

   
   
   
   
  1. using System;

  2. using System.ServiceModel;

  3. using System.ServiceModel.Channels;

  4. namespace Client

  5. {

  6. class Program

  7.    {

  8. staticvoid Main(string[] args)

  9.        {

  10.            //创建一个ChannelFactory,指定使用名为DataProvider的配置

  11.            var factory = new ChannelFactory<Server.IData>("DataProvider");

  12.            //指定用户名、密码,这个Test是服务端Windows上的一个普通帐户,如果加入了域,还要指定ClientCredential.Domain

  13.            factory.Credentials.Windows.ClientCredential.UserName= "Test";

  14.            factory.Credentials.Windows.ClientCredential.Password = "test";

  15.            //创建Channel,并调用SayHello方法

  16.            var proxy = factory.CreateChannel();

  17.            Console.WriteLine(proxy.SayHello());

  18.            ((IChannel)proxy).Close();

  19.        }

  20.    }

  21. }

其中的指定的用户名与密码是服务端存在的一个Windows用户。


OK,在两台机器上分别运行服务端和客户端,能够正常完成调用,输出“Hello Test-PC\Test”,这个Test-PC\Test就是我们调用时传递的用户身份,注意是“机器名\用户名”的形式,如果是AD环境,那么就是“域\用户名”的形式。

如果给定一个错误的用户名密码,则调用时会收到Exception:

   
   
   
   
  1. 未处理的异常:  System.ServiceModel.Security.SecurityNegotiationException: 调用方未由服务进行身份验证。 ---> System.ServiceModel.FaultException: 无法满足对安全令牌的请求,因为身份验证失败。

  2.   在 System.ServiceModel.Security.SecurityUtils.ThrowIfNegotiationFault(Message message, EndpointAddress target)

  3.   在 System.ServiceModel.Security.SspiNegotiationTokenProvider.GetNextOutgoingMessageBody(Message incomingMessage, SspiNegotiationTokenProviderState sspiState)

是一个身份验证失败的异常。


用Windows帐户来做验证,其实是个挺麻烦的事情,只有较少的系统是这么做的,我们还是希望能用更通用的用户名密码来进行身份验证,下一篇我们就来看看如何做。




你可能感兴趣的:(WCF)