演练:创建自定义客户端和服务凭据
本主题演示如何实现自定义客户端和服务凭据以及如何在应用程序代码中使用自定义凭据。
凭据扩展性类
ClientCredentials 和 ServiceCredentials 类是 Windows Communication Foundation (WCF) 安全扩展性的主要入口点。 这些凭据类提供 API,应用程序代码可以使用这些 API 来设置凭据信息和将凭据类型转换为安全令牌。 (安全令牌是用于在 SOAP 消息中传输凭据信息的形式。)这些凭据类的责任可以分成两部分:
为应用程序提供 API 以设置凭据信息。
用作 SecurityTokenManager 实现的工厂。
WCF 中提供的默认实现支持系统提供的凭据类型并可以创建能够处理这些凭据类型的安全令牌管理器。
自定义原因
自定义客户端或服务凭据类有多种原因。 最重要的原因是需要更改与处理系统提供的凭据类型有关的默认 WCF 安全行为,特别是由于以下原因:
无法使用其他扩展点进行的更改。
添加新的凭据类型。
添加新的自定义安全令牌类型。
本主题说明如何实现自定义客户端和服务凭据以及如何在应用程序代码中使用它们。
系列主题中的第一个主题
创建自定义凭据类只是第一步,因为自定义凭据是为了更改有关凭据预配、安全令牌序列化或身份验证的 WCF 行为。 本节中的其他主题说明如何创建自定义序列化程序和身份验证器。 在这一方面,创建自定义凭据类是系列主题中的第一个主题。 后续操作(创建自定义序列化程序和身份验证器)只有在创建自定义凭据后才能进行。 基于本主题的其他主题包括:
过程
实现自定义客户端凭据
定义一个从 ClientCredentials 类派生的新类。
可选。 为新凭据类型添加新方法或新属性。 如果未添加新凭据类型,请跳过此步骤。 下面的示例添加
CreditCardNumber
属性。重写 CreateSecurityTokenManager 方法。 在使用自定义客户端凭据时,WCF 安全基础结构将自动调用此方法。 此方法负责创建和返回 SecurityTokenManager 类实现的实例。
重要
需要特别注意的是,将重写 CreateSecurityTokenManager 方法以创建自定义安全令牌管理器。 派生自 ClientCredentialsSecurityTokenManager 的安全令牌管理器必须返回派生自 SecurityTokenProvider 的自定义安全令牌提供程序,才能创建实际的安全令牌。 如果不遵循此模式创建安全令牌,则当缓存 ChannelFactory 对象(这是 WCF 客户端代理的默认行为)时,您的应用程序可能无法正常工作,从而可能会导致面临特权提升攻击。 自定义凭据对象将作为 ChannelFactory 的一部分进行缓存。 然而,在每次调用时都会创建自定义 SecurityTokenManager,只要在 SecurityTokenManager 中放置了令牌创建逻辑,它就可以缓解安全威胁。
重写 CloneCore 方法。
public class MyClientCredentials : ClientCredentials { string creditCardNumber; public MyClientCredentials() { // Perform client credentials initialization. } protected MyClientCredentials(MyClientCredentials other) : base(other) { // Clone fields defined in this class. this.creditCardNumber = other.creditCardNumber; } public string CreditCardNumber { get { return this.creditCardNumber; } set { if (value == null) { throw new ArgumentNullException("value"); } this.creditCardNumber = value; } } public override SecurityTokenManager CreateSecurityTokenManager() { // Return your implementation of the SecurityTokenManager. return new MyClientCredentialsSecurityTokenManager(this); } protected override ClientCredentials CloneCore() { // Implement the cloning functionality. return new MyClientCredentials(this); } }
Public Class MyClientCredentials Inherits ClientCredentials Private creditCardNumberValue As String Public Sub New() End Sub ' Perform client credentials initialization. Protected Sub New(ByVal other As MyClientCredentials) MyBase.New(other) ' Clone fields defined in this class. Me.creditCardNumberValue = other.creditCardNumberValue End Sub Public Property CreditCardNumber() As String Get Return Me.creditCardNumberValue End Get Set If value Is Nothing Then Throw New ArgumentNullException("value") End If Me.creditCardNumberValue = value End Set End Property Public Overrides Function CreateSecurityTokenManager() As SecurityTokenManager ' Return your implementation of the SecurityTokenManager. Return New MyClientCredentialsSecurityTokenManager(Me) End Function Protected Overrides Function CloneCore() As ClientCredentials ' Implement the cloning functionality. Return New MyClientCredentials(Me) End Function End Class
实现自定义客户端安全令牌管理器
定义一个从 ClientCredentialsSecurityTokenManager 派生的新类。
可选。 如果必须创建一个自定义 CreateSecurityTokenProvider(SecurityTokenRequirement) 实现,则重写 SecurityTokenProvider 方法。 有关自定义安全令牌提供程序的详细信息,请参阅如何:创建自定义安全令牌提供程序。
可选。 如果必须创建一个自定义 CreateSecurityTokenAuthenticator(SecurityTokenRequirement, SecurityTokenResolver) 实现,则重写 SecurityTokenAuthenticator 方法。 有关自定义安全令牌身份验证器的详细信息,请参阅如何:创建自定义安全令牌身份验证器。
可选。 如果必须创建一个自定义 CreateSecurityTokenSerializer,则重写 SecurityTokenSerializer 方法。 有关自定义安全令牌和自定义安全令牌序列化程序的详细信息,请参阅如何:创建自定义令牌。
internal class MyClientCredentialsSecurityTokenManager : ClientCredentialsSecurityTokenManager { MyClientCredentials credentials; public MyClientCredentialsSecurityTokenManager(MyClientCredentials credentials) : base(credentials) { this.credentials = credentials; } public override SecurityTokenProvider CreateSecurityTokenProvider( SecurityTokenRequirement tokenRequirement) { // Return your implementation of the SecurityTokenProvider, if required. // This implementation delegates to the base class. return base.CreateSecurityTokenProvider(tokenRequirement); } public override SecurityTokenAuthenticator CreateSecurityTokenAuthenticator( SecurityTokenRequirement tokenRequirement, out SecurityTokenResolver outOfBandTokenResolver) { // Return your implementation of the SecurityTokenAuthenticator, if required. // This implementation delegates to the base class. return base.CreateSecurityTokenAuthenticator(tokenRequirement, out outOfBandTokenResolver); } public override SecurityTokenSerializer CreateSecurityTokenSerializer(SecurityTokenVersion version) { // Return your implementation of the SecurityTokenSerializer, if required. // This implementation delegates to the base class. return base.CreateSecurityTokenSerializer(version); } }
Friend Class MyClientCredentialsSecurityTokenManager Inherits ClientCredentialsSecurityTokenManager Private credentials As MyClientCredentials Public Sub New(ByVal credentials As MyClientCredentials) MyBase.New(credentials) Me.credentials = credentials End Sub Public Overrides Function CreateSecurityTokenProvider( _ ByVal tokenRequirement As SecurityTokenRequirement) As SecurityTokenProvider ' Return your implementation of the SecurityTokenProvider, if required. ' This implementation delegates to the base class. Return MyBase.CreateSecurityTokenProvider(tokenRequirement) End Function Public Overrides Function CreateSecurityTokenAuthenticator( _ ByVal tokenRequirement As SecurityTokenRequirement, _ ByRef outOfBandTokenResolver As SecurityTokenResolver) As SecurityTokenAuthenticator ' Return your implementation of the SecurityTokenAuthenticator, if required. ' This implementation delegates to the base class. Return MyBase.CreateSecurityTokenAuthenticator(tokenRequirement, outOfBandTokenResolver) End Function Public Overrides Function CreateSecurityTokenSerializer(ByVal version As SecurityTokenVersion) _ As SecurityTokenSerializer ' Return your implementation of the SecurityTokenSerializer, if required. ' This implementation delegates to the base class. Return MyBase.CreateSecurityTokenSerializer(version) End Function End Class
在应用程序代码中使用自定义客户端凭据
创建生成的表示服务接口的客户端的实例,或创建指向要与之通信的服务的 ChannelFactory 的实例。
创建自定义客户端凭据类的一个新实例并将其添加到 Behaviors 集合中,此集合可以通过 Endpoint 属性访问。
// Create a client with the client endpoint configuration. CalculatorClient client = new CalculatorClient(); // Remove the ClientCredentials behavior. client.ChannelFactory.Endpoint.Behaviors.Remove<ClientCredentials>(); // Add a custom client credentials instance to the behaviors collection. client.ChannelFactory.Endpoint.Behaviors.Add(new MyClientCredentials());
' Create a client with the client endpoint configuration. Dim client As New CalculatorClient() ' Remove the ClientCredentials behavior. client.ChannelFactory.Endpoint.Behaviors.Remove(Of ClientCredentials)() ' Add a custom client credentials instance to the behaviors collection. client.ChannelFactory.Endpoint.Behaviors.Add(New MyClientCredentials())
前面的过程演示如何从应用程序代码中使用客户端凭据。 WCF 凭据也可以使用应用程序配置文件进行配置。 使用应用程序配置通常比硬编码更可取,因为它允许更改应用程序参数而无需更改源代码、重新编译和重新部署。
下一个过程说明如何为配置自定义凭据提供支持。
创建自定义客户端凭据的配置处理程序
定义一个从 ClientCredentialsElement 派生的新类。
可选。 为所有其他要通过应用程序配置公开的配置参数添加属性。 下面的示例添加一个名为
CreditCardNumber
的属性。重写 BehaviorType 属性以返回使用配置元素创建的自定义客户端凭据类的类型。
重写 CreateBehavior 方法。 此方法负责根据从配置文件加载的设置创建和返回自定义凭据类的一个实例。 从此方法中调用基 ApplyConfiguration(ClientCredentials) 方法以检索加载到客户端凭据实例中的系统提供的凭据设置。
可选。 如果您在步骤 2 中添加了其他属性,则需要重写 Properties 属性,以便注册其他配置设置,使配置框架可以识别它们。 组合使用您的属性与基类属性可以通过此自定义客户端凭据配置元素来配置系统提供的设置。
public class MyClientCredentialsConfigHandler : ClientCredentialsElement { ConfigurationPropertyCollection properties; public override Type BehaviorType { get { return typeof(MyClientCredentials); } } public string CreditCardNumber { get { return (string)base["creditCardNumber"]; } set { if (String.IsNullOrEmpty(value)) { value = String.Empty; } base["creditCardNumber"] = value; } } protected override ConfigurationPropertyCollection Properties { get { if (this.properties == null) { ConfigurationPropertyCollection properties = base.Properties; properties.Add(new ConfigurationProperty( "creditCardNumber", typeof(System.String), string.Empty, null, new StringValidator(0, 32, null), ConfigurationPropertyOptions.None)); this.properties = properties; } return this.properties; } } protected override object CreateBehavior() { MyClientCredentials creds = new MyClientCredentials(); creds.CreditCardNumber = CreditCardNumber; base.ApplyConfiguration(creds); return creds; } }
Public Class MyClientCredentialsConfigHandler Inherits ClientCredentialsElement Private propertiesValue As ConfigurationPropertyCollection Public Overrides ReadOnly Property BehaviorType() As Type Get Return GetType(MyClientCredentials) End Get End Property Public Property CreditCardNumber() As String Get Return CStr(MyBase.Item("creditCardNumber")) End Get Set If String.IsNullOrEmpty(value) Then value = String.Empty End If MyBase.Item("creditCardNumber") = value End Set End Property Protected Overrides ReadOnly Property Properties() As ConfigurationPropertyCollection Get If Me.propertiesValue Is Nothing Then Dim myProperties As ConfigurationPropertyCollection = MyBase.Properties myProperties.Add(New ConfigurationProperty( _ "creditCardNumber", _ GetType(System.String), _ String.Empty, _ Nothing, _ New StringValidator(0, 32, Nothing), _ ConfigurationPropertyOptions.None)) Me.propertiesValue = myProperties End If Return Me.propertiesValue End Get End Property Protected Overrides Function CreateBehavior() As Object Dim creds As New MyClientCredentials() creds.CreditCardNumber = Me.CreditCardNumber MyBase.ApplyConfiguration(creds) Return creds End Function End Class
拥有配置处理程序类后,可以将它集成到 WCF 配置框架中。 这使自定义客户端凭据能够用于客户端终结点行为元素中,如下一个过程所示。
在应用程序配置中注册和使用自定义客户端凭据配置处理程序
将
<extensions>
元素添加到配置文件的<behaviorExtensions>
元素。将
<add>
元素添加到<behaviorExtensions>
元素,并将name
属性设置为适当的值。将
type
特性设置为完全限定类型名称。 此外还包括程序集名称和其他程序集属性。<system.serviceModel> <extensions> <behaviorExtensions> <add name="myClientCredentials" type="Microsoft.ServiceModel.Samples.MyClientCredentialsConfigHandler, CustomCredentials, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> </behaviorExtensions> </extensions> </system.serviceModel>
注册配置处理程序之后,可以在同一配置文件中使用自定义凭据元素来代替系统提供的
<clientCredentials>
元素。 可以同时使用系统提供的属性和任何添加到配置处理程序实现的新属性。 下面的示例使用creditCardNumber
属性设置自定义属性的值。<behaviors> <endpointBehaviors> <behavior name="myClientCredentialsBehavior"> <myClientCredentials creditCardNumber="123-123-123"/> </behavior> </endpointBehaviors> </behaviors>
实现自定义服务凭据
定义一个从 ServiceCredentials 派生的新类。
可选。 添加新属性以为将要添加的新凭据值提供 API。 如果未添加新凭据值,请跳过此步骤。 下面的示例添加
AdditionalCertificate
属性。重写 CreateSecurityTokenManager 方法。 在使用自定义客户端凭据时,WCF 基础结构自动调用此方法。 此方法负责创建和返回 SecurityTokenManager 类的实现的实例(在下一个过程中说明)。
可选。 重写 CloneCore 方法。 只有在将新属性或内部字段添加到自定义客户端凭据实现时,才需要此操作。
public class MyServiceCredentials : ServiceCredentials { X509Certificate2 additionalCertificate; public MyServiceCredentials() { } protected MyServiceCredentials(MyServiceCredentials other) : base(other) { this.additionalCertificate = other.additionalCertificate; } public X509Certificate2 AdditionalCertificate { get { return this.additionalCertificate; } set { if (value == null) { throw new ArgumentNullException("value"); } this.additionalCertificate = value; } } public override SecurityTokenManager CreateSecurityTokenManager() { return base.CreateSecurityTokenManager(); } protected override ServiceCredentials CloneCore() { return new MyServiceCredentials(this); } }
Public Class MyServiceCredentials Inherits ServiceCredentials Private additionalCertificateValue As X509Certificate2 Public Sub New() End Sub Protected Sub New(ByVal other As MyServiceCredentials) MyBase.New(other) Me.additionalCertificate = other.additionalCertificate End Sub Public Property AdditionalCertificate() As X509Certificate2 Get Return Me.additionalCertificateValue End Get Set If value Is Nothing Then Throw New ArgumentNullException("value") End If Me.additionalCertificateValue = value End Set End Property Public Overrides Function CreateSecurityTokenManager() As SecurityTokenManager Return MyBase.CreateSecurityTokenManager() End Function Protected Overrides Function CloneCore() As ServiceCredentials Return New MyServiceCredentials(Me) End Function End Class
实现自定义服务安全令牌管理器
定义一个从 ServiceCredentialsSecurityTokenManager 类派生的新类。
可选。 如果必须创建一个自定义 CreateSecurityTokenProvider 实现,则重写 SecurityTokenProvider 方法。 有关自定义安全令牌提供程序的详细信息,请参阅如何:创建自定义安全令牌提供程序。
可选。 如果必须创建一个自定义 CreateSecurityTokenAuthenticator 实现,则重写 SecurityTokenAuthenticator 方法。 有关自定义安全令牌身份验证器的详细信息,请参阅如何:创建自定义安全令牌身份验证器主题。
可选。 如果必须创建一个自定义 CreateSecurityTokenSerializer(SecurityTokenVersion),则重写 SecurityTokenSerializer 方法。 有关自定义安全令牌和自定义安全令牌序列化程序的详细信息,请参阅如何:创建自定义令牌。
internal class MyServiceCredentialsSecurityTokenManager : ServiceCredentialsSecurityTokenManager { MyServiceCredentials credentials; public MyServiceCredentialsSecurityTokenManager(MyServiceCredentials credentials) : base(credentials) { this.credentials = credentials; } public override SecurityTokenProvider CreateSecurityTokenProvider(SecurityTokenRequirement tokenRequirement) { // Return your implementation of SecurityTokenProvider, if required. // This implementation delegates to the base class. return base.CreateSecurityTokenProvider(tokenRequirement); } public override SecurityTokenAuthenticator CreateSecurityTokenAuthenticator(SecurityTokenRequirement tokenRequirement, out SecurityTokenResolver outOfBandTokenResolver) { // Return your implementation of SecurityTokenProvider, if required. // This implementation delegates to the base class. return base.CreateSecurityTokenAuthenticator(tokenRequirement, out outOfBandTokenResolver); } public override SecurityTokenSerializer CreateSecurityTokenSerializer(SecurityTokenVersion version) { // Return your implementation of SecurityTokenProvider, if required. // This implementation delegates to the base class. return base.CreateSecurityTokenSerializer(version); } }
Friend Class MyServiceCredentialsSecurityTokenManager Inherits ServiceCredentialsSecurityTokenManager Private credentials As MyServiceCredentials Public Sub New(ByVal credentials As MyServiceCredentials) MyBase.New(credentials) Me.credentials = credentials End Sub Public Overrides Function CreateSecurityTokenProvider(ByVal tokenRequirement As SecurityTokenRequirement) _ As SecurityTokenProvider ' Return your implementation of SecurityTokenProvider, if required. ' This implementation delegates to the base class. Return MyBase.CreateSecurityTokenProvider(tokenRequirement) End Function Public Overrides Function CreateSecurityTokenAuthenticator( _ ByVal tokenRequirement As SecurityTokenRequirement, _ ByRef outOfBandTokenResolver As SecurityTokenResolver) _ As SecurityTokenAuthenticator ' Return your implementation of SecurityTokenProvider, if required. ' This implementation delegates to the base class. Return MyBase.CreateSecurityTokenAuthenticator(tokenRequirement, outOfBandTokenResolver) End Function Public Overrides Function CreateSecurityTokenSerializer(ByVal version As SecurityTokenVersion) _ As SecurityTokenSerializer ' Return your implementation of SecurityTokenProvider, if required. ' This implementation delegates to the base class. Return MyBase.CreateSecurityTokenSerializer(version) End Function End Class
在应用程序代码中使用自定义服务凭据
创建 ServiceHost 的实例:
从 Behaviors 集合中删除系统提供的服务凭据行为。
创建自定义服务凭据类的一个新实例并将其添加到 Behaviors 集合中。
// Create a service host with a service type. ServiceHost serviceHost = new ServiceHost(typeof(Service)); // Remove the default ServiceCredentials behavior. serviceHost.Description.Behaviors.Remove<ServiceCredentials>(); // Add a custom service credentials instance to the collection. serviceHost.Description.Behaviors.Add(new MyServiceCredentials());
' Create a service host with a service type. Dim serviceHost As New ServiceHost(GetType(Service)) ' Remove the default ServiceCredentials behavior. serviceHost.Description.Behaviors.Remove(Of ServiceCredentials)() ' Add a custom service credentials instance to the collection. serviceHost.Description.Behaviors.Add(New MyServiceCredentials())
使用前面在过程“To create a configuration handler for custom client credentials
”和“To register and use a custom client credentials configuration handler in the application configuration
”中所述的步骤添加对配置的支持。唯一的区别是使用 ServiceCredentialsElement 类而不是 ClientCredentialsElement 类作为配置处理程序的基类。 这样,使用系统提供的 <serviceCredentials>
元素的任何地方都可以使用自定义服务凭据元素。