结束了服务认证的介绍之后,我们接着介绍WCF双向认证的另一个方面,即服务对客户端的认证,简称客户端认证。客户端认证采用的方式决定于客户端凭证的类型,内容只要涉及基于以下三种典型客户凭证类型的认证:Windows、用户名和X.509证书。从编程的角度来讲,Windows认证是最为简单的认证方式。在这种认证方式下,客户端进程运行的Window帐号对应的Windows凭证被自动作为调用服务的客户端凭证,所以无需显示指定具体的Windiws凭证。
如果需要另一个Windows帐号的名义调用服务,客户端就需要通知指定Windows帐号和密码的方式显式地进行客户端Windows凭证的设置。Windows凭证在WCF通过类型表示。下面给出了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属性,其类型为。我们知道String类型具有恒定性(Immutability),一旦被创建,它将在整个进程生命周期内一直存在。因此,如果某个String对象含有诸如密码、信用卡号码或个人数据等敏感信息,则因为应用程序无法从计算机内存中删除这些数据,便存在信息在使用后可能被泄漏的危险。SecureString对象与String对象的相似之处在于它也具有文本值。但是,SecureString对象的值是自动加密的,在应用程序将它标记为只读之前可以进行修改,并且可由应用程序或.NET Framework垃圾回收器将其从计算机内存中删除。
当你进行服务调用的时候,不管你是直接采用创建服务代理的方式,还是通过导入元数据生成客户端代理的方式,设置Windows凭证都很容易。ChannelFactory<TChannel>的基类ChannelFactory和ClientBase<TChannel>中都定义了一个只读属性ClientCredentials,该属性的类型为。对于类型ClientCredentials,我们应该不会感到陌生,因为在中我们通过它实现了对服务证书认证模式的改变。我们表示Windows凭证的WindowsClientCredential对象作为只读属性Windows定义在ClientCredentials中,相关类型的定义如下所示。
1: public class ChannelFactory: ChannelFactory
2: {
3: //省略成员
4: }
5: public abstract class ChannelFactory
6: {
7: //其他成员
8: public ClientCredentials Credentials { get; }
9: }
10: public abstract class ClientBase
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 (ChannelFactorychannelFactory = new ChannelFactory ("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来实现。