[WCF安全系列]谈谈WCF的客户端认证[Windows认证]
结束了服务认证的介绍之后,我们接着介绍WCF双向认证的另一个方面,即服务对客户端的认证,简称客户端认证。客户端认证采用的方式决定于客户端凭证的类型,内容只要涉及基于以下三种典型客户凭证类型的认证:Windows、用户名和X.509证书。从编程的角度来讲,Windows认证是最为简单的认证方式。在这种认证方式下,客户端进程运行的Window帐号对应的Windows凭证被自动作为调用服务的客户端凭证,所以无需显示指定具体的Windiws凭证。
如果需要另一个Windows帐号的名义调用服务,客户端就需要通知指定Windows帐号和密码的方式显式地进行客户端Windows凭证的设置。Windows凭证在WCF通过类型WindowsClientCredential表示。下面给出了WindowsClientCredential的定义,从中我们可以看到真正的凭证最终保存在类型为NetworkCredential的ClientCredential属性中。通过该属性,你可以指定Windows凭证的域名、用户名和密码。
1: public sealed class WindowsClientCredential
2: {
3: //其他成员
4: public bool AllowNtlm { get; set; }
5: public NetworkCredential ClientCredential { get; set; }
6: }
7: public class NetworkCredential : ICredentials, ICredentialsByHost
8: {
9: //其他成员
10: public string Domain { get; set; }
11: public string UserName { get; set; }
12: public string Password { get; set; }
13: public SecureString SecurePassword { get; set; }
14: }
从上面给出的代码我们可以看到,NetworkCredential除了一个以String类型表示的Password属性之外,还有另一外一个相关的SecurePassword属性,其类型为SecureString。我们知道String类型具有恒定性(Immutability),一旦被创建,它将在整个进程生命周期内一直存在。因此,如果某个String对象含有诸如密码、信用卡号码或个人数据等敏感信息,则因为应用程序无法从计算机内存中删除这些数据,便存在信息在使用后可能被泄漏的危险。SecureString对象与String对象的相似之处在于它也具有文本值。但是,SecureString对象的值是自动加密的,在应用程序将它标记为只读之前可以进行修改,并且可由应用程序或.NET Framework垃圾回收器将其从计算机内存中删除。
当你进行服务调用的时候,不管你是直接采用ChannelFactory<TChannel>创建服务代理的方式,还是通过导入元数据生成客户端代理的方式,设置Windows凭证都很容易。ChannelFactory<TChannel>的基类ChannelFactory和ClientBase<TChannel>中都定义了一个只读属性ClientCredentials,该属性的类型为ClientCredentials。对于类型ClientCredentials,我们应该不会感到陌生,因为在前面的实例演示中我们通过它实现了对服务证书认证模式的改变。我们表示Windows凭证的WindowsClientCredential对象作为只读属性Windows定义在ClientCredentials中,相关类型的定义如下所示。
1: public class ChannelFactory<TChannel> : ChannelFactory
2: {
3: //省略成员
4: }
5: public abstract class ChannelFactory
6: {
7: //其他成员
8: public ClientCredentials Credentials { get; }
9: }
10: public abstract class ClientBase<TChannel>
11: {
12: //其他成员
13: public ClientCredentials ClientCredentials { get; }
14: }
15: public class ClientCredentials : SecurityCredentialsManager, IEndpointBehavior
16: {
17: //其他成员
18: public WindowsClientCredential Windows { get; }
19: }
下面给出的代码片断为你演示了当你采用通过ChannelFactory<TChannel>创建的服务代理进行服务调用时如何进行Windows凭证的设置。
1: using (ChannelFactory<ICalculator> channelFactory = new ChannelFactory<ICalculator>("calculatorService"))
2: {
3: NetworkCredential credential = channelFactory.Credentials.Windows.ClientCredential;
4: credential.Domain = "DomainName";
5: credential.UserName = "UserName";
6: credential.Password = "Password";
7:
8: ICalculator calculator = channelFactory.CreateChannel();
9: double result = calculator.Add(1, 2);
10: ...
11: }
关于WCF下的Windows认证,还有一点值得一提。上面我们给出了WindowsClientCredential的定义,不知道你是否注意到了它具有一个布尔类型的属性AllowNtlm。这个属性实际上涉及到关于Windows认证协议的问题。WCF集成的Windows认证是基于SSPI(Security Support Provider Interface),这是一套标准的安全编程接口,而具体安全功能的实现定义在相应的SSP(Security Support Provider)。SSPI是面向接口的安全编程成为可能,这样的好处显而易见:在基于不同SSP的安全环境中,你的程序都能兼容。
Windows提供了三种典型的SSP:Kerberos、NTLMSSP和SPNEGO。前两种分别基于我们熟悉的Kerberos和NTLM,但是SPNEGO才是默认的选项。SPNEGO的全名为“Simple and Protected GSSAPI Negotiation Mechanism”,而GSSAPI(Generic Security Services Application Program Interface)是互联网工程任务组(IETF)指定的保准的安全应用编程接口。SPNEGO,故名思义,就是通过协商(Negotiation)确定一种适合的GSS API。SPNEGO在Windows下的协商机制是这样的:首选Kerberos,如果不可用则退而求其次,选用NTLM。
不论从安全性还是互操作性(实际上Kerberos本身就是一种标准),Kerberos都要优于NTLM,但是Keberos仅限于基于AD的域环境中使用。如果你强制要求只采用Kerberos认证,你可以通过将WindowsClientCredential的AllowNtlm属性设成False来实现。