第八篇:WCF安全

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

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

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

1、服务端

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

   
   
   
   
  1. xml version="1.0" encoding="utf-8" ?> 
  2. <configuration> 
  3. <system.serviceModel> 
  4.    <services> 
  5.       <service name="Server.DataProvider"> 
  6.  
  7.         <endpoint address="" binding="netTcpBinding" contract="Server.IData" bindingConfiguration="tcpBinding" /> 
  8.         <host> 
  9.           <baseAddresses> 
  10.             <add baseAddress="net.tcp://localhost:8081/wcf" /> 
  11.           baseAddresses> 
  12.         host> 
  13.       service> 
  14.     services> 
  15.  
  16.      
  17.     <bindings> 
  18.  
  19.       <netTcpBinding> 
  20.  
  21.         <binding name="tcpBinding"> 
  22.  
  23.           <security mode="Message"> 
  24.  
  25.             <message clientCredentialType="Windows" /> 
  26.           security> 
  27.         binding> 
  28.       netTcpBinding> 
  29.     bindings> 
  30.   system.serviceModel> 
  31. configuration> 

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

   
   
   
   
  1. using System; 
  2. using System.ServiceModel; 
  3.  
  4. namespace Server 
  5.     [ServiceBehavior] 
  6.     public class DataProvider : IData 
  7.     { 
  8.         public string SayHello() 
  9.         { 
  10. //WindowsIdentity即代表访问者的身份标识 
  11.             return string.Format("Hello {0}", OperationContext.Current.ServiceSecurityContext.WindowsIdentity.Name); 
  12.         } 
  13.     } 


2、客户端

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

   
   
   
   
  1. xml version="1.0" encoding="utf-8" ?> 
  2. <configuration> 
  3.   <system.serviceModel> 
  4.     <client> 
  5.  
  6.       <endpoint binding="netTcpBinding" contract="Server.IData" address="net.tcp://192.168.90.90:8081/wcf" name="DataProvider" bindingConfiguration="tcp" /> 
  7.     client> 
  8.  
  9.     <bindings> 
  10.       <netTcpBinding> 
  11.         <binding name="tcp"> 
  12.           <security mode="Message"> 
  13.             <message clientCredentialType="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.  
  5. namespace Client 
  6.     class Program 
  7.     { 
  8.         static void Main(string[] args) 
  9.         { 
  10. //创建一个ChannelFactory,指定使用名为DataProvider的配置 
  11.             var factory = new ChannelFactory("DataProvider"); 
  12.  
  13. //指定用户名、密码,这个Test是服务端Windows上的一个普通帐户,如果加入了域,还要指定ClientCredential.Domain
  14. factory.Credentials.Windows.ClientCredential.UserName= "Test"
  15. factory.Credentials.Windows.ClientCredential.Password = "test"
  16.  
  17. //创建Channel,并调用SayHello方法 
  18.             var proxy = factory.CreateChannel(); 
  19.             Console.WriteLine(proxy.SayHello()); 
  20.             ((IChannel)proxy).Close(); 
  21.         } 
  22.     } 

其中的指定的用户名与密码是服务端存在的一个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帐户来做验证,其实是个挺麻烦的事情,只有较少的系统是这么做的,我们还是希望能用更通用的用户名密码来进行身份验证,下一篇我们就来看看如何做。