逐步解說:建立自訂用戶端與服務認證

此主題顯示如何實作自訂用戶端和服務認證,以及如何使用來自應用程式碼的自訂認證。

認證擴充性類別

ClientCredentialsServiceCredentials 類別是 Windows Communication Foundation (WCF) 安全性擴充性的主要進入點。 這些認證類別提供能夠讓應用程式碼設定認證資訊,以及將認證類型轉換為安全性權杖的 API (安全性權杖是用來在 SOAP 訊息內部傳輸認證資訊的型式。)這些認證類別的責任可以分為兩部分:

  • 提供 API 讓應用程式設定認證資訊。

  • 執行當做 SecurityTokenManager 實作的處理站。

WCF 中提供的預設實作會支援系統提供的認證類型,並建立能夠處理這些認證類型的安全性權杖管理員。

自訂原因

有幾項自訂用戶端或服務認證類別的原因。 首要需求莫過於變更涉及處理系統提供之認證類型的預設 WCF 安全性行為,特別是基於下列原因:

  • 使用其他擴充點無法進行若干變更。

  • 為了加入新的認證類型。

  • 為了加入新的自訂安全性權杖類型。

此主題描述如何實作自訂用戶端和服務認證,以及如何在應用程式碼使用它們。

第一步

建立自訂認證類別只是第一步,因為自訂認證的原因是為了變更有關認證佈建、安全性權杖序列化或驗證的 WCF 行為。 本章節中的其他主題描述如何建立自訂序列化程式和驗證器。 就這一點而言,建立自訂認證類別是所有步驟的第一個主題。 只有在建立自訂認證後才能完成後續動作 (建立自訂序列化程式和驗證器)。 建構在此主題上的其他主題包含:

程序

實作自訂用戶端認證

  1. 定義衍生自 ClientCredentials 類別的新類別。

  2. 選擇性。 為新的認證類型加入新方法或屬性。 如果您不需要新增新的認證類型,請略過這個步驟。 下列範例即是加入 CreditCardNumber 屬性。

  3. 覆寫 CreateSecurityTokenManager 方法。 當使用自訂用戶端認證時,WCF 安全性基礎結構會自動呼叫此方法。 這個方法是負責建立和傳回 SecurityTokenManager 類別實作的執行個體。

    重要

    請注意,覆寫 CreateSecurityTokenManager 方法以建立自訂安全性權杖管理員是很重要的。 衍生自 ClientCredentialsSecurityTokenManager 的安全性權杖管理員,必須傳回衍生自 SecurityTokenProvider 的自訂安全性權杖提供者,以建立實際的安全性權杖。 若未依照這個模式建立安全性權杖,應用程式在快取 ChannelFactory 物件時 (此乃 WCF 用戶端 Proxy 的預設行為) 可能會運作不正常,以致難免遭受提高權限攻擊。 自訂認證物件是快取為 ChannelFactory 的一部分。 不過,自訂 SecurityTokenManager 則為每次叫用時所建立,因而只要將權仗建立邏輯置於 SecurityTokenManager 中,便能減緩安全性威脅。

  4. 覆寫 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
    

實作自訂用戶端安全性權杖管理員

  1. 定義衍生自 ClientCredentialsSecurityTokenManager 的新類別。

  2. 選擇性。 如果必須建立自訂 CreateSecurityTokenProvider(SecurityTokenRequirement) 實作,請覆寫 SecurityTokenProvider 方法。 如需自訂安全性權杖提供者的詳細資訊,請參閱操作說明:建立自訂安全性權杖提供者

  3. 選擇性。 如果必須建立自訂 CreateSecurityTokenAuthenticator(SecurityTokenRequirement, SecurityTokenResolver) 實作,請覆寫 SecurityTokenAuthenticator 方法。 如需自訂安全性權杖驗證器的詳細資訊,請參閱操作說明:建立自訂安全性權杖驗證器

  4. 選擇性。 如果必須建立自訂 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
    

使用來自應用程式碼的自訂用戶端認證

  1. 建立所產生之用戶端的執行個體以代表服務介面,或建立 ChannelFactory 的執行個體指向想要通訊的服務。

  2. Behaviors 集合中移除系統提供的用戶端認證行為,您可以透過 Endpoint 屬性存取這個集合。

  3. 建立自訂用戶端認證類別的新執行個體,並將其新增至 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 認證也可以使用應用程式組態檔進行設定。 在硬式編碼方面通常偏好使用應用程式組態,因為能夠在不修改原始程式碼、重新編譯和重新部署的情況下修改應用程式參數。

下一個程序描述如何提供自訂認證組態的支援。

建立自訂用戶端認證的組態處理常式

  1. 定義衍生自 ClientCredentialsElement 的新類別。

  2. 選擇性。 針對想要透過應用程式組態公開的所有其他組態參數新增屬性。 下列範例會新增一個名為 CreditCardNumber 的屬性。

  3. 覆寫 BehaviorType 屬性以傳回使用組態項目建立之自訂用戶端認證類別的型別。

  4. 覆寫 CreateBehavior 方法。 這個方法會根據從組態檔載入的設定,負責建立和傳回自訂認證類別的執行個體。 請呼叫這個方法的基底 ApplyConfiguration(ClientCredentials) 方法,擷取已載入自訂用戶端認證執行個體之系統提供的認證設定。

  5. 選擇性。 如果您在步驟 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 組態架構中。 就能夠在用戶端端點行為項目中使用自訂用戶端認證,如同下一個程序中所示。

在應用程式組態中登錄和使用自訂用戶端認證組態處理常式

  1. <extensions> 元素和 <behaviorExtensions> 元素新增至組態檔。

  2. <add> 元素新增至 <behaviorExtensions> 元素,並將 name 屬性設定為適當的值。

  3. 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>
    
  4. 登錄組態處理常式之後,就可以在相同的組態檔內使用自訂認證元素,而不是系統提供的 <clientCredentials> 元素。 您可以同時使用系統提供的屬性,以及新增至組態處理常式實作的任何新屬性。 下列程式碼範例會使用 creditCardNumber 屬性 (Attribute) 設定自訂屬性 (Property) 的值。

    <behaviors>
      <endpointBehaviors>
        <behavior name="myClientCredentialsBehavior">
          <myClientCredentials creditCardNumber="123-123-123"/>
        </behavior>
      </endpointBehaviors>
    </behaviors>
    

實作自訂服務認證

  1. 定義衍生自 ServiceCredentials 的新類別。

  2. 選擇性。 新增新的屬性以提供已新增之新認證值的 API。 如果您不需要新增新的認證值,請略過這個步驟。 下列程式碼範例會新增 AdditionalCertificate 屬性。

  3. 覆寫 CreateSecurityTokenManager 方法。 當使用自訂用戶端認證時,WCF 基礎結構會自動呼叫此方法。 這個方法是負責建立和傳回 SecurityTokenManager 類別實作的執行個體 (在下一個程序中會描述)。

  4. 選擇性。 覆寫 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
    

實作自訂服務安全性權杖管理員

  1. 定義衍生自 ServiceCredentialsSecurityTokenManager 類別的新類別。

  2. 選擇性。 如果必須建立自訂 CreateSecurityTokenProvider 實作,請覆寫 SecurityTokenProvider 方法。 如需自訂安全性權杖提供者的詳細資訊,請參閱操作說明:建立自訂安全性權杖提供者

  3. 選擇性。 如果必須建立自訂 CreateSecurityTokenAuthenticator 實作,請覆寫 SecurityTokenAuthenticator 方法。 如需自訂安全性權杖驗證器的詳細資訊,請參閱操作說明:建立自訂安全性權杖驗證器主題。

  4. 選擇性。 如果必須建立自訂 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
    

使用來自應用程式碼的自訂服務認證

  1. 建立 ServiceHost的執行個體。

  2. Behaviors 集合移除系統提供的服務認證行為。

  3. 建立自訂服務認證類別的新執行個體,並將其新增至 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> 項目的地方使用自訂服務認證項目。

另請參閱