WCF 安全编程
本主题描述用于创建安全的 Windows Communication Foundation (WCF) 应用程序的基本编程任务。 本主题只讨论身份验证、保密性和完整性,它们统称为传输安全性。 本主题不讨论授权(资源或服务的访问控制);有关授权的信息,请参阅授权。
备注
有关安全概念(尤其是与 WCF 有关的安全概念)的有用介绍,请参阅 MSDN 上的 Web 服务增强 (WSE) 3.0 的方案、模式和实现指南中的一系列模式和实践教程。
WCF 安全编程基于三个步骤,它们分别设置以下方面:安全模式、客户端凭据类型和凭据值。 可以通过代码或配置执行这些步骤。
设置安全模式
下面解释了 WCF 中的安全模式编程的常规步骤:
选择一个适合于应用程序要求的预定义绑定。 有关绑定选项的列表,请参阅系统提供的绑定。 默认情况下,几乎每个绑定都启用了安全。 一个例外是 BasicHttpBinding 类(使用配置时,为 <basicHttpBinding>)。
所选的绑定确定了传输协议。 例如,WSHttpBinding 使用 HTTP 传输协议;而 NetTcpBinding 使用 TCP 传输协议。
为绑定选择一个安全模式。 请注意,所选的绑定确定了可以进行的模式选择。 例如,WSDualHttpBinding 不允许启用传输安全(它不是选项)。 同样,MsmqIntegrationBinding 和 NetNamedPipeBinding 都不允许启用消息安全。
您有三个选择:
Transport
传输安全取决于所选绑定使用的机制。 例如,如果要使用
WSHttpBinding
,则安全机制是安全套接字层 (SSL)(它也是 HTTPS 协议的机制)。 一般说来,传输安全的主要优点是它提供了较高的吞吐量,而无论您使用哪种传输协议。 但是,它确实具有两个限制:第一个限制是传输机制指示了用于对用户进行身份验证的凭据类型。 只有当服务需要与其他要求不同类型凭据的服务交互操作时,这才是一个缺点。 第二个限制是,因为安全不是在消息级应用的,所以安全是逐个跃点实现的,而不是以端对端方式实现的。 只有当客户端和服务之间的消息路径包含中介时,后一个限制才会成为问题。 有关使用哪种传输方式的详细信息,请参阅选择传输方式。 有关使用传输安全性的详细信息,请参阅传输安全性概述。Message
消息安全意味着每个消息都包含必要的标头和数据,以保证消息的安全。 因为标头的组成千变万化,所以可以包含任意数量的凭据。 如果您要与其他要求传输机制无法提供的特定凭据类型的服务交互操作,或者如果必须将消息用于多个服务(其中每个服务都要求不同的凭据类型),那么这会是一个有用的办法。
有关详细信息,请参阅消息安全性。
TransportWithMessageCredential
此选择使用传输层来保证消息传输的安全,同时每个消息都包含其他服务需要的丰富凭据。 这便将传输安全的性能优点与消息安全的丰富凭据优点结合起来。 使用下列绑定可实现这一点:BasicHttpBinding、WSFederationHttpBinding、NetPeerTcpBinding 和 WSHttpBinding。
如果决定对 HTTP 使用传输安全(即 HTTPS),还必须用 SSL 证书配置主机并且在端口上启用 SSL。 有关详细信息,请参阅 HTTP 传输安全性。
如果您要使用 WSHttpBinding 并且不需要建立安全会话,请将 EstablishSecurityContext 属性设置为
false
。当客户端和服务使用对称密钥创建通道时(客户端和服务器在整个对话过程中都使用相同的密钥,直到对话结束),将发生安全会话。
设置客户端凭据类型
选择适当的客户端凭据类型。 有关详细信息,请参阅选择凭据类型。 下列客户端凭据类型可用:
Windows
Certificate
Digest
Basic
UserName
NTLM
IssuedToken
根据您设置模式的方式的不同,必须设置凭据类型。 例如,如果您已经选择了 wsHttpBinding
,并且将模式设置为“Message”,则还可以将 Message 元素的 clientCredentialType
属性设置为下列值之一:None
、Windows
、UserName
、Certificate
和 IssuedToken
,如下面的配置示例所示。
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="myBinding">
<security mode="Message"/>
<message clientCredentialType="Windows"/>
</binding>
</wsHttpBinding>
</bindings>
</system.serviceModel>
或者在代码中:
WSHttpBinding b = new WSHttpBinding();
b.Name = "myBinding";
b.Security.Mode = SecurityMode.Message;
b.Security.Message.ClientCredentialType=MessageCredentialType.Windows;
Dim b As New WSHttpBinding()
b.Name = "myBinding"
b.Security.Mode = SecurityMode.Message
b.Security.Message.ClientCredentialType = MessageCredentialType.Windows
设置服务凭据值
一旦选择了客户端凭据类型,就必须设置可供服务和客户端使用的实际凭据。 在服务上,使用 ServiceCredentials 类设置凭据,并且由 Credentials 类的 ServiceHostBase 属性返回。 所使用的绑定暗示了服务凭据类型、选择的安全模式和客户端凭据类型。 下面的代码为服务凭据设置了证书。
// Create the binding for an endpoint.
NetTcpBinding b = new NetTcpBinding();
b.Security.Mode = SecurityMode.Message;
// Create the ServiceHost for a calculator.
Uri baseUri = new Uri("net.tcp://MachineName/tcpBase");
Uri[] baseAddresses = new Uri[] { baseUri };
ServiceHost sh = new ServiceHost(typeof(Calculator), baseAddresses);
// Add an endpoint using the binding and a new address.
Type c = typeof(ICalculator);
sh.AddServiceEndpoint(c, b, "MyEndpoint");
// Set a certificate as the credential for the service.
sh.Credentials.ServiceCertificate.SetCertificate(
StoreLocation.LocalMachine,
StoreName.My,
X509FindType.FindBySubjectName,
"client.com");
try
{
sh.Open();
Console.WriteLine("Listening....");
Console.ReadLine();
sh.Close();
}
catch (CommunicationException ce)
{
Console.WriteLine("A communication error occurred: {0}", ce.Message);
Console.WriteLine();
}
catch (System.Exception exc)
{
Console.WriteLine("An unforeseen error occurred: {0}", exc.Message);
Console.ReadLine();
}
' Create the binding for an endpoint.
Dim b As New NetTcpBinding()
b.Security.Mode = SecurityMode.Message
' Create the ServiceHost for a calculator.
Dim baseUri As New Uri("net.tcp://MachineName/tcpBase")
Dim baseAddresses() As Uri = {baseUri}
Dim sh As New ServiceHost(GetType(Calculator), baseAddresses)
' Add an endpoint using the binding and a new address.
Dim c As Type = GetType(ICalculator)
sh.AddServiceEndpoint(c, b, "MyEndpoint")
' Set a certificate as the credential for the service.
sh.Credentials.ServiceCertificate.SetCertificate( _
StoreLocation.LocalMachine, _
StoreName.My, _
X509FindType.FindBySubjectName, _
"contoso.com")
Try
sh.Open()
Console.WriteLine("Listening....")
Console.ReadLine()
sh.Close()
Catch ce As CommunicationException
Console.WriteLine("A communication error occurred: {0}", ce.Message)
Console.WriteLine()
Catch exc As System.Exception
Console.WriteLine("An unforeseen error occurred: {0}", exc.Message)
Console.ReadLine()
End Try
设置客户端凭据值
在客户端上,使用 ClientCredentials 类设置客户端凭据值,并且通过 ClientCredentials 类的 ClientBase<TChannel> 属性返回该值。 下面的代码在使用 TCP 协议的客户端上将证书设置为凭据。
// Create a NetTcpBinding and set its security properties. The
// security mode is Message, and the client must be authenticated with
// Windows. Therefore the client must be on the same Windows domain.
NetTcpBinding b = new NetTcpBinding();
b.Security.Mode = SecurityMode.Message;
b.Security.Message.ClientCredentialType = MessageCredentialType.Windows;
// Set a Type variable for use when constructing the endpoint.
Type c = typeof(ICalculator);
// Create a base address for the service.
Uri tcpBaseAddress =
new Uri("net.tcp://machineName.Domain.Contoso.com:8036/serviceName");
// The base address is in an array of URI objects.
Uri[] baseAddresses = new Uri[] { tcpBaseAddress };
// Create the ServiceHost with type and base addresses.
ServiceHost sh = new ServiceHost(typeof(CalculatorClient), baseAddresses);
// Add an endpoint to the service using the service type and binding.
sh.AddServiceEndpoint(c, b, "");
sh.Open();
string address = sh.Description.Endpoints[0].ListenUri.AbsoluteUri;
Console.WriteLine("Listening @ {0}", address);
Console.WriteLine("Press enter to close the service");
Console.ReadLine();
' Create a NetTcpBinding and set its security properties. The
' security mode is Message, and the client must be authenticated with
' Windows. Therefore the client must be on the same Windows domain.
Dim b As New NetTcpBinding()
b.Security.Mode = SecurityMode.Message
b.Security.Message.ClientCredentialType = MessageCredentialType.Windows
' Set a Type variable for use when constructing the endpoint.
Dim c As Type = GetType(ICalculator)
' Create a base address for the service.
Dim tcpBaseAddress As New Uri("net.tcp://machineName.Domain.Contoso.com:8036/serviceName")
' The base address is in an array of URI objects.
Dim baseAddresses() As Uri = {tcpBaseAddress}
' Create the ServiceHost with type and base addresses.
Dim sh As New ServiceHost(GetType(CalculatorClient), baseAddresses)
' Add an endpoint to the service using the service type and binding.
sh.AddServiceEndpoint(c, b, "")
sh.Open()
Dim address As String = sh.Description.Endpoints(0).ListenUri.AbsoluteUri
Console.WriteLine("Listening @ {0}", address)
Console.WriteLine("Press enter to close the service")
Console.ReadLine()