語言

如何:建立自訂的安全令牌驗證器

本主題將說明如何建立自訂的安全令牌驗證器,以及如何將其與自訂安全令牌管理器整合。 安全令牌驗證器會驗證隨收到訊息而提供的安全令牌內容。 若驗證成功,認證器會回傳一組 IAuthorizationPolicy 實例,評估後回傳一組權利要求。

要在 Windows 通訊基金會(WCF)中使用自訂的安全令牌驗證器,您必須先建立自訂憑證與安全令牌管理器實作。 欲了解更多關於建立自訂憑證與安全憑證管理器的資訊,請參閱 「攻略:建立自訂客戶端與服務憑證」。

程序

建立自訂安全令牌驗證器

  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
    

另請參閱