Пошаговое руководство. Создание пользовательских учетных данных для клиента и службы

В этом разделе показано, как реализовать пользовательские учетные данные клиента и службы, и как использовать пользовательские учетные данные из кода приложения.

Классы расширяемости учетных данных

ServiceCredentials И ClientCredentials классы являются основными точками входа в расширяемость безопасности Windows Communication Foundation (WCF). Эти классы учетных данных предоставляют интерфейсы API, позволяющие коду приложения задавать учетные данные и преобразовывать типы учетных данных в маркеры безопасности. (Маркеры безопасности — это форма, используемая для передачи учетных данных в сообщения SOAP.) Обязанности этих классов учетных данных можно разделить на две области:

  • предоставление интерфейсов API, позволяющих приложениям задавать учетные данные;

  • выступать в качестве фабрики для реализаций SecurityTokenManager.

Реализации по умолчанию, предоставляемые в WCF, поддерживают типы учетных данных, предоставляемые системой, и создают диспетчер маркеров безопасности, способный обрабатывать эти типы учетных данных.

Причины для настройки учетных данных

Существует причин настраивать классы учетных данных как клиентов, так и служб. Прежде всего, необходимо изменить поведение безопасности WCF по умолчанию в отношении обработки системных типов учетных данных, особенно по следующим причинам:

  • Изменения, невозможные при использовании других точек расширяемости.

  • Добавление новых типов учетных данных.

  • Добавление новых пользовательских типов маркеров безопасности.

В этом разделе описывается реализация пользовательских учетных данных клиента и службы и их использование из кода приложения.

Дальнейшие шаги

Создание пользовательского класса учетных данных является лишь первым шагом, так как причина настройки учетных данных заключается в изменении поведения WCF в отношении подготовки учетных данных, сериализации маркеров безопасности или проверки подлинности. В других подразделах этого раздела описывается создание пользовательских сериализаторов и структур проверки подлинности. В этом отношении создание пользовательского класса учетных данных - первый подраздел серии. Следующие действия (создание пользовательских сериализаторов и структур проверки подлинности) можно предпринимать только после создания пользовательских учетных данных. Содержание данного подраздела продолжают следующие подразделы:

Процедуры

Реализация пользовательских учетных данных клиента

  1. Определите новый класс, производный от класса ClientCredentials.

  2. Необязательно. Добавьте новые методы или свойства для новых типов учетных данных. Если новые типы учетных данных не добавляются, пропустите этот шаг. В следующем примере добавляется свойство CreditCardNumber.

  3. Переопределите метод CreateSecurityTokenManager. Этот метод автоматически вызывается инфраструктурой безопасности WCF при использовании пользовательских учетных данных клиента. Он отвечает за создание и возвращение экземпляра реализации класса SecurityTokenManager.

    Внимание

    Важно отметить, что метод CreateSecurityTokenManager переопределен, чтобы создать пользовательский диспетчер маркеров безопасности. Чтобы создать фактический маркер безопасности, диспетчер маркеров безопасности, производный от ClientCredentialsSecurityTokenManager, должен возвратить пользовательский поставщик маркеров безопасности, производный от SecurityTokenProvider. Если не следовать этому шаблону при создании маркеров безопасности, приложение может работать неправильно, если объекты ChannelFactory кэшируются (кэширование применяется по умолчанию в клиентских прокси WCF). В этом случае также возможна атака с несанкционированным получением прав. Объект пользовательских учетных данных кэшируется в составе 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 задается значение пользовательского свойства.

    <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>.

См. также