Практическое руководство. Создание пользовательского аутентификатора токенов безопасности

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

Чтобы использовать пользовательский аутентификатор токенов безопасности в Windows Communication Foundation (WCF), необходимо сначала создать пользовательские учетные данные и реализации менеджера токенов безопасности. Дополнительные сведения о создании пользовательских учетных данных и диспетчере маркеров безопасности см. в пошаговом руководстве по созданию пользовательских учетных данных клиента и службы.

Procedures

Создание пользовательского аутентификатора для токена безопасности

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

  2. Переопределите метод CanValidateTokenCore. Метод возвращает true или false в зависимости от того, может ли пользовательский аутентификатор проверить тип входящего токена или нет.

  3. Переопределите метод ValidateTokenCore. Этот метод должен правильно проверить содержимое токена. Если маркер проходит этап проверки, он возвращает коллекцию IAuthorizationPolicy экземпляров. В следующем примере используется пользовательская реализация политики авторизации, которая будет создана в следующей процедуре.

    internal class MySecurityTokenAuthenticator : SecurityTokenAuthenticator
    {
        protected override bool CanValidateTokenCore(SecurityToken token)
        {
            // Check that the incoming token is a username token type that
            // can be validated by this implementation.
            return (token is UserNameSecurityToken);
        }
    
        protected override ReadOnlyCollection<IAuthorizationPolicy>
            ValidateTokenCore(SecurityToken token)
        {
            UserNameSecurityToken userNameToken = token as UserNameSecurityToken;
    
            // Validate the information contained in the username token. For demonstration
            // purposes, this code just checks that the user name matches the password.
            if (userNameToken.UserName != userNameToken.Password)
            {
                throw new SecurityTokenValidationException("Invalid user name or password");
            }
    
            // Create just one Claim instance for the username token - the name of the user.
            DefaultClaimSet userNameClaimSet = new DefaultClaimSet(
                ClaimSet.System,
                new Claim(ClaimTypes.Name, userNameToken.UserName, Rights.PossessProperty));
            List<IAuthorizationPolicy> policies = new List<IAuthorizationPolicy>(1);
            policies.Add(new MyAuthorizationPolicy(userNameClaimSet));
            return policies.AsReadOnly();
        }
    }
    
    Friend Class MySecurityTokenAuthenticator
        Inherits SecurityTokenAuthenticator
    
        Protected Overrides Function CanValidateTokenCore(ByVal token As SecurityToken) As Boolean
            ' Check that the incoming token is a username token type that  
            ' can be validated by this implementation.
            Return (TypeOf token Is UserNameSecurityToken)
        End Function
    
        Protected Overrides Function ValidateTokenCore(ByVal token As SecurityToken) As ReadOnlyCollection(Of IAuthorizationPolicy)
    
            Dim userNameToken = TryCast(token, UserNameSecurityToken)
    
            ' Validate the information contained in the username token. For demonstration 
            ' purposes, this code just checks that the user name matches the password.
            If userNameToken.UserName <> userNameToken.Password Then
                Throw New SecurityTokenValidationException("Invalid user name or password")
            End If
    
            ' Create just one Claim instance for the username token - the name of the user.
            Dim userNameClaimSet As New DefaultClaimSet(ClaimSet.System, _
                                                        New Claim(ClaimTypes.Name, _
                                                        userNameToken.UserName, _
                                                        Rights.PossessProperty))
            Dim policies As New List(Of IAuthorizationPolicy)(1)
            policies.Add(New MyAuthorizationPolicy(userNameClaimSet))
            Return policies.AsReadOnly()
        End Function
    
    End Class
    

Предыдущий код возвращает коллекцию политик авторизации в методе CanValidateToken(SecurityToken) . WCF не предоставляет общедоступную реализацию этого интерфейса. В следующей процедуре показано, как это сделать для ваших собственных требований.

Создание настраиваемой политики авторизации

  1. Определите новый класс, реализующий IAuthorizationPolicy интерфейс.

  2. Id Создайте свойство, доступное только для чтения. Одним из способов реализации этого свойства является создание глобально уникального идентификатора (GUID) в конструкторе классов и возврат его при каждом запросе значения этого свойства.

  3. Issuer Создайте свойство, доступное только для чтения. Это свойство должно возвращать издателя наборов утверждений, полученных из маркера. Этот издатель должен соответствовать издателю токена или центральному органу, ответственному за проверку содержимого токена. В следующем примере используется утверждение издателя, передаваемое этому классу из пользовательского средства проверки подлинности маркера безопасности, созданного в предыдущей процедуре. Пользовательский аутентификатор токенов безопасности использует предоставленный системой набор утверждений (возвращенный свойством System) для представления эмитента токена имени пользователя.

  4. Реализуйте метод Evaluate. Этот метод заполняет экземпляр EvaluationContext класса (переданный в качестве аргумента) утверждениями, основанными на содержимом входящего маркера безопасности. Метод возвращает true по завершении оценки. В случаях, когда реализация зависит от наличия других политик авторизации, которые предоставляют дополнительные сведения в контексте оценки, этот метод может вернуться false , если необходимые сведения еще не присутствуют в контексте оценки. В этом случае WCF снова вызовет метод после оценки всех других политик авторизации, созданных для входящего сообщения, если по крайней мере одна из этих политик авторизации изменила контекст оценки.

    internal class MyAuthorizationPolicy : IAuthorizationPolicy
    {
        string id;
        ClaimSet tokenClaims;
        ClaimSet issuer;
    
        public MyAuthorizationPolicy(ClaimSet tokenClaims)
        {
            if (tokenClaims == null)
            {
                throw new ArgumentNullException("tokenClaims");
            }
            this.issuer = tokenClaims.Issuer;
            this.tokenClaims = tokenClaims;
            this.id = Guid.NewGuid().ToString();
        }
    
        public ClaimSet Issuer
        {
            get { return issuer; }
        }
    
        public string Id
        {
            get { return id; }
        }
    
        public bool Evaluate(EvaluationContext evaluationContext, ref object state)
        {
            // Add the token claim set to the evaluation context.
            evaluationContext.AddClaimSet(this, tokenClaims);
    
            // Return true if the policy evaluation is finished.
            return true;
        }
    }
    
    Friend Class MyAuthorizationPolicy
        Implements IAuthorizationPolicy
    
        Private _id As String
        Private _tokenClaims As ClaimSet
        Private _issuer As ClaimSet
    
        Public Sub New(ByVal tokenClaims As ClaimSet)
            If _tokenClaims Is Nothing Then
                Throw New ArgumentNullException("tokenClaims")
            End If
            Me._issuer = tokenClaims.Issuer
            Me._tokenClaims = tokenClaims
            Me._id = Guid.NewGuid().ToString()
        End Sub
    
        Public ReadOnly Property Issuer() As ClaimSet Implements IAuthorizationPolicy.Issuer
            Get
                Return _issuer
            End Get
        End Property
    
        Public ReadOnly Property Id() As String Implements System.IdentityModel.Policy.IAuthorizationComponent.Id
            Get
                Return _id
            End Get
        End Property
    
        Public Function Evaluate(ByVal evaluationContext As EvaluationContext, _
                                 ByRef state As Object) As Boolean Implements IAuthorizationPolicy.Evaluate
    
            ' Add the token claim set to the evaluation context.
            evaluationContext.AddClaimSet(Me, _tokenClaims)
            ' Return true if the policy evaluation is finished.
            Return True
        End Function
    
    End Class
    

Пошаговое руководство. Создание пользовательских учетных данных клиента и службы описывает создание пользовательских учетных данных и пользовательского диспетчера маркеров безопасности. Чтобы использовать пользовательское средство проверки подлинности маркера безопасности, созданное в этом месте, реализация менеджера маркеров безопасности изменяется, чтобы вернуть пользовательское средство проверки подлинности из CreateSecurityTokenAuthenticator метода. Метод возвращает аутентификатор, когда передано соответствующее требование маркера безопасности.

Для интеграции настраиваемого аутентификатора маркера безопасности с настраиваемым менеджером маркеров безопасности

  1. Переопределите CreateSecurityTokenAuthenticator метод в реализации пользовательского менеджера токенов безопасности.

  2. Добавьте логику в метод, чтобы он возвращал ваше настраиваемое средство аутентификации маркеров безопасности на основе параметра SecurityTokenRequirement. В следующем примере возвращается настроенный проверяющий маркера безопасности, если тип требуемого токена - имя пользователя (представлено свойством UserName), а направление сообщения, для которого запрашивается аутентификатор токена безопасности, является входным (представлено полем Input).

    internal class MyServiceCredentialsSecurityTokenManager :
        ServiceCredentialsSecurityTokenManager
    {
        ServiceCredentials credentials;
        public MyServiceCredentialsSecurityTokenManager(ServiceCredentials credentials)
            : base(credentials)
        {
            this.credentials = credentials;
        }
    
        public override SecurityTokenAuthenticator CreateSecurityTokenAuthenticator
            (SecurityTokenRequirement tokenRequirement, out SecurityTokenResolver outOfBandTokenResolver)
        {
            // Return your implementation of the SecurityTokenProvider based on the
            // tokenRequirement argument.
            SecurityTokenAuthenticator result;
            if (tokenRequirement.TokenType == SecurityTokenTypes.UserName)
            {
                MessageDirection direction = tokenRequirement.GetProperty<MessageDirection>
                    (ServiceModelSecurityTokenRequirement.MessageDirectionProperty);
                if (direction == MessageDirection.Input)
                {
                    outOfBandTokenResolver = null;
                    result = new MySecurityTokenAuthenticator();
                }
                else
                {
                    result = base.CreateSecurityTokenAuthenticator(tokenRequirement, out outOfBandTokenResolver);
                }
            }
            else
            {
                result = base.CreateSecurityTokenAuthenticator(tokenRequirement, out outOfBandTokenResolver);
            }
    
            return result;
        }
    }
    
    Friend Class MyServiceCredentialsSecurityTokenManager
        Inherits ServiceCredentialsSecurityTokenManager
    
        Private credentials As ServiceCredentials
    
        Public Sub New(ByVal credentials As ServiceCredentials)
            MyBase.New(credentials)
            Me.credentials = credentials
        End Sub
    
        Public Overrides Function CreateSecurityTokenAuthenticator(ByVal tokenRequirement As SecurityTokenRequirement, _
                                                                   <System.Runtime.InteropServices.Out()> _
                                                                   ByRef outOfBandTokenResolver _
                                                                   As SecurityTokenResolver) As SecurityTokenAuthenticator
            ' Return your implementation of the SecurityTokenProvider based on the 
            ' tokenRequirement argument.
            Dim result As SecurityTokenAuthenticator
            If tokenRequirement.TokenType = SecurityTokenTypes.UserName Then
                Dim direction = tokenRequirement.GetProperty(Of MessageDirection)(ServiceModelSecurityTokenRequirement.MessageDirectionProperty)
                If direction = MessageDirection.Input Then
                    outOfBandTokenResolver = Nothing
                    result = New MySecurityTokenAuthenticator()
                Else
                    result = MyBase.CreateSecurityTokenAuthenticator(tokenRequirement, _
                                                                     outOfBandTokenResolver)
                End If
            Else
                result = MyBase.CreateSecurityTokenAuthenticator(tokenRequirement, _
                                                                 outOfBandTokenResolver)
            End If
    
            Return result
        End Function
    
    End Class
    

См. также