如何:创建自定义令牌

本主题介绍如何使用 SecurityToken 类创建自定义安全令牌,以及如何将其与自定义安全令牌提供程序和身份验证器进行集成。

安全令牌**实质上是一个 XML 元素,Windows Communication Foundation (WCF) 安全框架使用它来表示与 SOAP 消息内部发送方有关的声明。WCF 安全为系统提供的身份验证模式提供各种令牌。包括由 X509SecurityToken 类表示的 X.509 证书安全令牌,或由 UserNameSecurityToken 类表示的用户名安全令牌。

有时,所提供的类型不支持某种身份验证模式或凭据。这种情况下,必须创建自定义安全令牌来提供 SOAP 消息内部自定义凭据的 XML 表示形式。

下面的过程演示如何创建自定义安全令牌,以及如何将其与 WCF 安全基础结构进行集成。本主题创建一个信用卡令牌,用于将客户端的信用卡相关信息传递到服务器。

有关自定义凭据和安全令牌管理器的更多信息,请参见演练:创建自定义客户端和服务凭据

若要了解更多表示安全令牌的类,请参见 System.IdentityModel.Tokens 命名空间。

有关凭据、安全令牌管理器、提供程序和身份验证器类的更多信息,请参见安全体系结构

过程

客户端应用程序必须有一种方式来指定安全基础结构的信用卡信息。应用程序通过自定义客户端凭据类可访问此信息。第一步是创建一个类,用以表示自定义客户端凭据的信用卡信息。

创建一个表示客户端凭据内信用卡信息的类

  1. 定义一个新类,该类在应用程序中表示信用卡信息。下面的示例将该类命名为 CreditCardInfo

  2. 向类添加相应的属性,以便应用程序可以设置自定义令牌所需的必要信息。在此示例中,该类具有三个属性:CardNumberCardIssuerExpirationDate

    Public Class CreditCardInfo
    
        Private _cardNumber As String
        Private _cardIssuer As String
        Private _expirationDate As DateTime
    
        Public Sub New(ByVal cardNumber As String, ByVal cardIssuer As String, _
                       ByVal expirationDate As DateTime)
            Me._cardNumber = cardNumber
            Me._cardIssuer = cardIssuer
            Me._expirationDate = expirationDate
        End Sub
    
        Public ReadOnly Property CardNumber() As String
            Get
                Return Me._cardNumber
            End Get
        End Property
    
        Public ReadOnly Property CardIssuer() As String
            Get
                Return Me._cardIssuer
            End Get
        End Property
    
        Public ReadOnly Property ExpirationDate() As DateTime
            Get
                Return Me._expirationDate
            End Get
        End Property
    
    End Class
    
    public class CreditCardInfo
    {
        string cardNumber;
        string cardIssuer;
        DateTime expirationDate;
    
        public CreditCardInfo(string cardNumber, string cardIssuer, DateTime expirationDate)
        {
            this.cardNumber = cardNumber;
            this.cardIssuer = cardIssuer;
            this.expirationDate = expirationDate;
        }
    
        public string CardNumber
        {
            get { return this.cardNumber; }
        }
    
        public string CardIssuer
        {
            get { return this.cardIssuer; }
        }
    
        public DateTime ExpirationDate
        {
            get { return this.expirationDate; }
        }
    }
    

接下来,必须创建一个表示自定义安全令牌的类。安全令牌提供程序、身份验证器和序列化程序类将使用该类与 WCF 安全基础结构相互传递安全令牌的有关信息。

创建自定义安全令牌类

  1. 定义一个从 SecurityToken 类派生的新类。此示例创建一个名为 CreditCardToken 的类。

  2. 重写 Id 属性。该属性用于获取安全令牌的本地标识符,本地标识符用于从 SOAP 消息内其他元素指向安全令牌 XML 表示形式。在本示例中,令牌标识符可以作为构造函数参数传递给该属性,也可以在每次创建安全令牌实例时随机生成一个新的令牌标识符。

  3. 实现 SecurityKeys 属性。该属性返回一个安全密钥集合,这些密钥是安全令牌实例表示的。WCF 可以使用这些密钥对 SOAP 消息的组成部分进行签名或加密。在本示例中,信用卡安全令牌不能包含任何安全密钥;因此,该实现始终返回一个空集合。

  4. 重写 ValidFromValidTo 属性。WCF 使用这些属性来确定安全令牌实例的有效性。在本示例中,信用卡安全令牌只有到期日期,因此,ValidFrom 属性返回一个 DateTime,它表示实例的创建日期和时间。

    Friend Class CreditCardToken
        Inherits SecurityToken
    
        Private _cardInfo As CreditCardInfo
        Private _effectiveTime As DateTime = DateTime.UtcNow
        Private _id As String
        Private _securityKeys As ReadOnlyCollection(Of SecurityKey)
    
        Public Sub New(ByVal cardInfo As CreditCardInfo)
            Me.New(cardInfo, Guid.NewGuid().ToString())
        End Sub
    
        Public Sub New(ByVal cardInfo As CreditCardInfo, _
                       ByVal id As String)
            If cardInfo Is Nothing Then
                Throw New ArgumentNullException("cardInfo")
            End If
            If id Is Nothing Then
                Throw New ArgumentNullException("id")
            End If
    
            Me._cardInfo = cardInfo
            Me._id = id
            ' The credit card token is not capable of any cryptography.
            Me._securityKeys = New ReadOnlyCollection(Of SecurityKey)(New List(Of SecurityKey)())
        End Sub
    
        Public ReadOnly Property CardInfo() As CreditCardInfo
            Get
                Return Me._cardInfo
            End Get
        End Property
    
        Public Overrides ReadOnly Property SecurityKeys() As ReadOnlyCollection(Of SecurityKey)
            Get
                Return Me._securityKeys
            End Get
        End Property
    
        Public Overrides ReadOnly Property ValidFrom() As DateTime
            Get
                Return Me._effectiveTime
            End Get
        End Property
    
        Public Overrides ReadOnly Property ValidTo() As DateTime
            Get
                Return Me._cardInfo.ExpirationDate
            End Get
        End Property
    
        Public Overrides ReadOnly Property Id() As String
            Get
                Return Me._id
            End Get
        End Property
    End Class
    
    class CreditCardToken : SecurityToken
    {
        CreditCardInfo cardInfo;
        DateTime effectiveTime = DateTime.UtcNow;
        string id;
        ReadOnlyCollection<SecurityKey> securityKeys;
    
        public CreditCardToken(CreditCardInfo cardInfo) : this(cardInfo, Guid.NewGuid().ToString()) { }
    
        public CreditCardToken(CreditCardInfo cardInfo, string id)
        {
            if (cardInfo == null)
            {
                throw new ArgumentNullException("cardInfo");
            }
            if (id == null)
            {
                throw new ArgumentNullException("id");
            }
    
            this.cardInfo = cardInfo;
            this.id = id;
            // The credit card token is not capable of any cryptography.
            this.securityKeys = new ReadOnlyCollection<SecurityKey>(new List<SecurityKey>());
        }
    
        public CreditCardInfo CardInfo 
        { 
            get { return this.cardInfo; } 
        }
    
        public override ReadOnlyCollection<SecurityKey> SecurityKeys 
        { 
            get { return this.securityKeys; } 
        }
    
        public override DateTime ValidFrom 
        { 
            get { return this.effectiveTime; } 
        }
    
        public override DateTime ValidTo 
        { 
            get { return this.cardInfo.ExpirationDate; } 
        }
    
        public override string Id 
        { 
            get { return this.id; } 
        }
    }
    

在创建新的安全令牌类型时,需要实现 SecurityTokenParameters 类。该实现在安全绑定元素配置中用于表示新的令牌类型。安全令牌参数类用作模板,用于在处理消息时与实际安全令牌实例进行匹配。该模板提供附加属性,应用程序可以使用这些附加属性来指定标准,安全令牌必须符合该标准才能使用或进行身份验证。下面的示例未添加任何附加属性,因此,当 WCF 基础结构搜索要使用或验证的安全令牌实例时,只会匹配安全令牌类型。

创建自定义安全令牌参数类

  1. 定义一个从 SecurityTokenParameters 类派生的新类。

  2. 实现 CloneCore 方法。复制类中定义的所有内部字段(如果有)。此示例未定义任何附加字段。

  3. 实现 SupportsClientAuthentication 只读属性。如果该类表示的安全令牌类型可以用来向服务验证客户端身份,则该属性返回 true。在本示例中,信用卡安全令牌可以用来向服务验证客户端身份。

  4. 实现 SupportsServerAuthentication 只读属性。如果该类表示的安全令牌类型可以用来向客户端验证服务身份,则该属性返回 true。在本示例中,信用卡安全令牌不能用来向客户端验证服务身份。

  5. 实现 SupportsClientWindowsIdentity 只读属性。如果该类表示的安全令牌类型可以映射到 Windows 帐户,则该属性返回 true。这种情况下,身份验证结果由一个 WindowsIdentity 类实例表示。在本示例中,令牌无法映射到 Windows 帐户。

  6. 实现 CreateKeyIdentifierClause 方法。如果 WCF 安全框架需要引用此安全令牌参数类所表示的安全令牌实例,则会调用此方法。实际安全令牌实例和 SecurityTokenReferenceStyle(指定所请求的引用类型)都作为参数传递到此方法。在本示例中,信用卡安全令牌仅支持内部引用。SecurityToken 类具有创建内部引用的功能,因此,该实现不需要附加代码。

  7. 实现 InitializeSecurityTokenRequirement 方法。WCF 调用此方法将安全令牌参数类实例转换为 SecurityTokenRequirement 类的实例。安全令牌提供程序使用转换结果来创建相应的安全令牌实例。

    Public Class CreditCardTokenParameters
        Inherits SecurityTokenParameters
    
        Public Sub New()
        End Sub
    
        Protected Sub New(ByVal other As CreditCardTokenParameters)
            MyBase.New(other)
        End Sub
    
        Protected Overrides Function CloneCore() As SecurityTokenParameters
            Return New CreditCardTokenParameters(Me)
        End Function
    
        Protected Overrides Sub InitializeSecurityTokenRequirement(ByVal requirement As SecurityTokenRequirement)
            requirement.TokenType = Constants.CreditCardTokenType
            Return
        End Sub
    
        ' A credit card token has no cryptography, no windows identity, and supports only client authentication.
        Protected Overrides ReadOnly Property HasAsymmetricKey() As Boolean
            Get
                Return False
            End Get
        End Property
    
        Protected Overrides ReadOnly Property SupportsClientAuthentication() As Boolean
            Get
                Return True
            End Get
        End Property
    
        Protected Overrides ReadOnly Property SupportsClientWindowsIdentity() As Boolean
            Get
                Return False
            End Get
        End Property
    
        Protected Overrides ReadOnly Property SupportsServerAuthentication() As Boolean
            Get
                Return False
            End Get
        End Property
    
        Protected Overrides Function CreateKeyIdentifierClause(ByVal token As SecurityToken, _
                                                               ByVal referenceStyle As SecurityTokenReferenceStyle) As SecurityKeyIdentifierClause
            If referenceStyle = SecurityTokenReferenceStyle.Internal Then
                Return token.CreateKeyIdentifierClause(Of LocalIdKeyIdentifierClause)()
            Else
                Throw New NotSupportedException("External references are not supported for credit card tokens")
            End If
        End Function
    
    End Class
    
    public class CreditCardTokenParameters : SecurityTokenParameters
    {
        public CreditCardTokenParameters()
        {
        }
    
        protected CreditCardTokenParameters(CreditCardTokenParameters other)
            : base(other)
        {
        }
    
        protected override SecurityTokenParameters CloneCore()
        {
            return new CreditCardTokenParameters(this);
        }
    
        protected override void InitializeSecurityTokenRequirement(SecurityTokenRequirement requirement)
        {
            requirement.TokenType = Constants.CreditCardTokenType;
            return;
        }
    
        // A credit card token has no cryptography, no windows identity, and supports only client authentication.
        protected override bool HasAsymmetricKey 
        { 
            get { return false; } 
        }
    
        protected override bool SupportsClientAuthentication 
        { 
            get { return true; } 
        }
    
        protected override bool SupportsClientWindowsIdentity 
        { 
            get { return false; } 
        }
    
        protected override bool SupportsServerAuthentication 
        { 
            get { return false; } 
        }
    
        protected override SecurityKeyIdentifierClause CreateKeyIdentifierClause(SecurityToken token, SecurityTokenReferenceStyle referenceStyle)
        {
            if (referenceStyle == SecurityTokenReferenceStyle.Internal)
            {
                return token.CreateKeyIdentifierClause<LocalIdKeyIdentifierClause>();
            }
            else
            {
                throw new NotSupportedException("External references are not supported for credit card tokens");
            }
        }
    }
    

安全令牌被传递到 SOAP 消息内部,因此,在安全令牌的内存表示形式和网络表示形式之间,需要一种转换机制。WCF 使用安全令牌序列化程序来完成这一任务。每个自定义令牌都必须有一个自定义安全令牌序列化程序,用于对 SOAP 消息中的自定义安全令牌进行序列化和反序列化。

ms731872.note(zh-cn,VS.100).gif注意:
默认情况下启用派生密钥。如果创建自定义安全令牌并将其用作主令牌,则 WCF 将从其派生一个密钥。如果这样做,则它将调用自定义安全令牌序列化程序以便为自定义安全令牌写入 SecurityKeyIdentifierClause,同时将 DerivedKeyToken 序列化到网络。在接收端,如果从网络反序列化令牌,则 DerivedKeyToken 序列化程序期望 SecurityTokenReference 元素作为其下的顶级子元素。如果自定义安全令牌序列化程序在序列化其子句类型时未添加 SecurityTokenReference 元素,则会引发异常。

创建自定义安全令牌序列化程序

  1. 定义一个从 WSSecurityTokenSerializer 类派生的新类。

  2. 重写 CanReadTokenCore 方法,该方法使用一个 XmlReader 来读取 XML 流。如果序列化程序实现可以根据给定的当前元素对安全令牌进行反序列化,则该方法返回 true。在本示例中,该方法检查 XML 读取器的当前 XML 元素是否具有正确的元素名称和命名空间。如果不具有,则调用该方法的基类实现来处理该 XML 元素。

  3. 重写 ReadTokenCore 方法。此方法读取安全令牌的 XML 内容,并为其构造相应的内存表示形式。如果它不能识别传入的 XML 读取器当前读取的 XML 元素,则会调用基类实现来处理系统提供的令牌类型。

  4. 重写 CanWriteTokenCore 方法。如果可以将令牌的内存表示形式(作为参数传入)转换为 XML 表示形式,则此方法返回 true。如果无法转换,则会调用基类实现。

  5. 重写 WriteTokenCore 方法。此方法将安全令牌的内存表示形式转换为 XML 表示形式。如果此方法无法转换,则会调用基类实现。

    Public Class CreditCardSecurityTokenSerializer
        Inherits WSSecurityTokenSerializer
    
        Public Sub New(ByVal version As SecurityTokenVersion)
            MyBase.New()
        End Sub
    
        Protected Overrides Function CanReadTokenCore(ByVal reader As XmlReader) As Boolean
            Dim localReader = XmlDictionaryReader.CreateDictionaryReader(reader)
            If reader Is Nothing Then
                Throw New ArgumentNullException("reader")
            End If
            If reader.IsStartElement(Constants.CreditCardTokenName, _
                                     Constants.CreditCardTokenNamespace) Then
                Return True
            End If
            Return MyBase.CanReadTokenCore(reader)
        End Function
    
        Protected Overrides Function ReadTokenCore(ByVal reader As XmlReader, _
                                                   ByVal tokenResolver As SecurityTokenResolver) As SecurityToken
            If reader Is Nothing Then
                Throw New ArgumentNullException("reader")
            End If
            If reader.IsStartElement(Constants.CreditCardTokenName, _
                                     Constants.CreditCardTokenNamespace) Then
    
                Dim id = reader.GetAttribute(Constants.Id, _
                                             Constants.WsUtilityNamespace)
                reader.ReadStartElement()
    
                ' Read the credit card number.
                Dim creditCardNumber = reader.ReadElementString(Constants.CreditCardNumberElementName, _
                                                                Constants.CreditCardTokenNamespace)
    
                ' Read the expiration date.
                Dim expirationTimeString = reader.ReadElementString(Constants.CreditCardExpirationElementName, _
                                                                    Constants.CreditCardTokenNamespace)
                Dim expirationTime As DateTime = XmlConvert.ToDateTime(expirationTimeString, _
                                                                       XmlDateTimeSerializationMode.Utc)
    
                ' Read the issuer of the credit card.
                Dim creditCardIssuer = reader.ReadElementString(Constants.CreditCardIssuerElementName, _
                                                                Constants.CreditCardTokenNamespace)
                reader.ReadEndElement()
    
                Dim cardInfo As New CreditCardInfo(creditCardNumber, _
                                                   creditCardIssuer, _
                                                   expirationTime)
    
                Return New CreditCardToken(cardInfo, id)
            Else
                Return WSSecurityTokenSerializer.DefaultInstance.ReadToken(reader, _
                                                                           tokenResolver)
            End If
        End Function
    
        Protected Overrides Function CanWriteTokenCore(ByVal token As SecurityToken) As Boolean
            If TypeOf token Is CreditCardToken Then
                Return True
            Else
                Return MyBase.CanWriteTokenCore(token)
            End If
        End Function
    
        Protected Overrides Sub WriteTokenCore(ByVal writer As XmlWriter, _
                                               ByVal token As SecurityToken)
            If writer Is Nothing Then
                Throw New ArgumentNullException("writer")
            End If
            If token Is Nothing Then
                Throw New ArgumentNullException("token")
            End If
    
            Dim c = TryCast(token, CreditCardToken)
            If c IsNot Nothing Then
                With writer
                    .WriteStartElement(Constants.CreditCardTokenPrefix, _
                                       Constants.CreditCardTokenName, _
                                       Constants.CreditCardTokenNamespace)
                    .WriteAttributeString(Constants.WsUtilityPrefix, _
                                          Constants.Id, _
                                          Constants.WsUtilityNamespace, _
                                          token.Id)
                    .WriteElementString(Constants.CreditCardNumberElementName, _
                                        Constants.CreditCardTokenNamespace, _
                                        c.CardInfo.CardNumber)
                    .WriteElementString(Constants.CreditCardExpirationElementName, _
                                        Constants.CreditCardTokenNamespace, _
                                        XmlConvert.ToString(c.CardInfo.ExpirationDate, _
                                                            XmlDateTimeSerializationMode.Utc))
                    .WriteElementString(Constants.CreditCardIssuerElementName, _
                                        Constants.CreditCardTokenNamespace, _
                                        c.CardInfo.CardIssuer)
                    .WriteEndElement()
                    .Flush()
                End With
            Else
                MyBase.WriteTokenCore(writer, token)
            End If
        End Sub
    
    End Class
    
    public class CreditCardSecurityTokenSerializer : WSSecurityTokenSerializer
    {
        public CreditCardSecurityTokenSerializer(SecurityTokenVersion version) : base() { }
    
        protected override bool CanReadTokenCore(XmlReader reader)
        {
            XmlDictionaryReader localReader = XmlDictionaryReader.CreateDictionaryReader(reader);
            if (reader == null)
            {
                throw new ArgumentNullException("reader");
            }
            if (reader.IsStartElement(Constants.CreditCardTokenName, Constants.CreditCardTokenNamespace))
            {
                return true;
            }
            return base.CanReadTokenCore(reader);
        }
    
        protected override SecurityToken ReadTokenCore(XmlReader reader, SecurityTokenResolver tokenResolver)
        {
            if (reader == null)
            {
                throw new ArgumentNullException("reader");
            }
            if (reader.IsStartElement(Constants.CreditCardTokenName, Constants.CreditCardTokenNamespace))
            {
                string id = reader.GetAttribute(Constants.Id, Constants.WsUtilityNamespace);
    
                reader.ReadStartElement();
    
                // Read the credit card number.
                string creditCardNumber = reader.ReadElementString(Constants.CreditCardNumberElementName, Constants.CreditCardTokenNamespace);
    
                // Read the expiration date.
                string expirationTimeString = reader.ReadElementString(Constants.CreditCardExpirationElementName, Constants.CreditCardTokenNamespace);
                DateTime expirationTime = XmlConvert.ToDateTime(expirationTimeString, XmlDateTimeSerializationMode.Utc);
    
                // Read the issuer of the credit card.
                string creditCardIssuer = reader.ReadElementString(Constants.CreditCardIssuerElementName, Constants.CreditCardTokenNamespace);
                reader.ReadEndElement();
    
                CreditCardInfo cardInfo = new CreditCardInfo(creditCardNumber, creditCardIssuer, expirationTime);
    
                return new CreditCardToken(cardInfo, id);
            }
            else
            {
                return WSSecurityTokenSerializer.DefaultInstance.ReadToken(reader, tokenResolver);
            }
        }
    
        protected override bool CanWriteTokenCore(SecurityToken token)
        {
            if (token is CreditCardToken)
            {
                return true;
            }
            else
            {
                return base.CanWriteTokenCore(token);
            }
        }
    
        protected override void WriteTokenCore(XmlWriter writer, SecurityToken token)
        {
            if (writer == null) 
            { 
                throw new ArgumentNullException("writer"); 
            }
            if (token == null) 
            { 
                throw new ArgumentNullException("token"); 
            }
    
            CreditCardToken c = token as CreditCardToken;
            if (c != null)
            {
                writer.WriteStartElement(Constants.CreditCardTokenPrefix, Constants.CreditCardTokenName, Constants.CreditCardTokenNamespace);
                writer.WriteAttributeString(Constants.WsUtilityPrefix, Constants.Id, Constants.WsUtilityNamespace, token.Id);
                writer.WriteElementString(Constants.CreditCardNumberElementName, Constants.CreditCardTokenNamespace, c.CardInfo.CardNumber);
                writer.WriteElementString(Constants.CreditCardExpirationElementName, Constants.CreditCardTokenNamespace, XmlConvert.ToString(c.CardInfo.ExpirationDate, XmlDateTimeSerializationMode.Utc));
                writer.WriteElementString(Constants.CreditCardIssuerElementName, Constants.CreditCardTokenNamespace, c.CardInfo.CardIssuer);
                writer.WriteEndElement();
                writer.Flush();
            }
            else
            {
                base.WriteTokenCore(writer, token);
            }
        }
    }
    

完成前面四个过程之后,将自定义安全令牌与安全令牌提供程序、身份验证器、管理器以及客户端和服务凭据进行集成。

将自定义安全令牌与安全令牌提供程序进行集成

  1. 安全令牌提供程序可以创建、修改(如果需要)和返回令牌的实例。若要创建自定义安全令牌的自定义提供程序,请创建一个从 SecurityTokenProvider 类继承的类。下面的示例重写 GetTokenCore 方法以返回 CreditCardToken 的实例。有关自定义安全令牌提供程序的更多信息,请参见如何:创建自定义安全令牌提供程序

    Friend Class CreditCardTokenProvider
        Inherits SecurityTokenProvider
    
        Private creditCardInfo As CreditCardInfo
    
        Public Sub New(ByVal creditCardInfo As CreditCardInfo)
            MyBase.New()
            If creditCardInfo Is Nothing Then
                Throw New ArgumentNullException("creditCardInfo")
            End If
            Me.creditCardInfo = creditCardInfo
        End Sub
    
        Protected Overrides Function GetTokenCore(ByVal timeout As TimeSpan) As SecurityToken
            Return TryCast(New CreditCardToken(Me.creditCardInfo), SecurityToken)
        End Function
    
    End Class
    
    class CreditCardTokenProvider : SecurityTokenProvider
    {
        CreditCardInfo creditCardInfo;
    
        public CreditCardTokenProvider(CreditCardInfo creditCardInfo)
            : base()
        {
            if (creditCardInfo == null)
            {
                throw new ArgumentNullException("creditCardInfo");
            }
            this.creditCardInfo = creditCardInfo;
        }
    
        protected override SecurityToken GetTokenCore(TimeSpan timeout)
        {
            SecurityToken result = new CreditCardToken(this.creditCardInfo);
            return result;
        }
    }
    

将自定义安全令牌与安全令牌身份验证器进行集成

  1. 当安全令牌从消息中提取出来,安全令牌身份验证器即对其内容进行验证。若要为自定义安全令牌创建自定义身份验证器,请创建一个从 SecurityTokenAuthenticator 类继承的类。下面的示例重写 ValidateTokenCore 方法。有关自定义安全令牌身份验证器的更多信息,请参见如何:创建自定义安全令牌身份验证器

    Friend Class CreditCardTokenAuthenticator
        Inherits SecurityTokenAuthenticator
    
        Private creditCardsFile As String
    
        Public Sub New(ByVal creditCardsFile As String)
            Me.creditCardsFile = creditCardsFile
        End Sub
    
        Protected Overrides Function CanValidateTokenCore(ByVal token As SecurityToken) As Boolean
            Return (TypeOf token Is CreditCardToken)
        End Function
    
        Protected Overrides Function ValidateTokenCore(ByVal token As SecurityToken) As ReadOnlyCollection(Of IAuthorizationPolicy)
    
            Dim creditCardToken = TryCast(token, CreditCardToken)
    
            If creditCardToken.CardInfo.ExpirationDate < DateTime.UtcNow Then
                Throw New SecurityTokenValidationException("The credit card has expired")
            End If
            If Not IsCardNumberAndExpirationValid(creditCardToken.CardInfo) Then
                Throw New SecurityTokenValidationException("Unknown or invalid credit card")
            End If
    
            ' The credit card token has only 1 claim: the card number. The issuer for the claim is the
            ' credit card issuer.
            Dim cardIssuerClaimSet As New DefaultClaimSet(New Claim(ClaimTypes.Name, _
                                                                    creditCardToken.CardInfo.CardIssuer, _
                                                                    Rights.PossessProperty))
            Dim cardClaimSet As New DefaultClaimSet(cardIssuerClaimSet, _
                                                    New Claim(Constants.CreditCardNumberClaim, _
                                                              creditCardToken.CardInfo.CardNumber, _
                                                              Rights.PossessProperty))
            Dim policies As New List(Of IAuthorizationPolicy)(1)
            policies.Add(New CreditCardTokenAuthorizationPolicy(cardClaimSet))
            Return policies.AsReadOnly()
        End Function
    
        ' This helper method checks whether a given credit card entry is present in the user database.
        Private Function IsCardNumberAndExpirationValid(ByVal cardInfo As CreditCardInfo) As Boolean
            Try
                Using myStreamReader As New StreamReader(Me.creditCardsFile)
                    Dim line = String.Empty
                    line = myStreamReader.ReadLine()
                    Do While line IsNot Nothing
                        Dim splitEntry() = line.Split("#"c)
                        If splitEntry(0) = cardInfo.CardNumber Then
                            Dim expirationDateString = splitEntry(1).Trim()
                            Dim expirationDateOnFile As DateTime = DateTime.Parse(expirationDateString, _
                                                                                  System.Globalization.DateTimeFormatInfo.InvariantInfo, _
                                                                                  System.Globalization.DateTimeStyles.AdjustToUniversal)
                            If cardInfo.ExpirationDate = expirationDateOnFile Then
                                Dim issuer = splitEntry(2)
                                Return issuer.Equals(cardInfo.CardIssuer, _
                                                     StringComparison.InvariantCultureIgnoreCase)
                            Else
                                Return False
                            End If
                        End If
                        line = myStreamReader.ReadLine()
                    Loop
                    Return False
                End Using
            Catch e As Exception
                Throw New Exception("BookStoreService: Error while retrieving credit card information from User DB " & e.ToString())
            End Try
        End Function
    
    End Class
    
    class CreditCardTokenAuthenticator : SecurityTokenAuthenticator
    {
        string creditCardsFile;
        public CreditCardTokenAuthenticator(string creditCardsFile)
        {
            this.creditCardsFile = creditCardsFile;
        }
    
        protected override bool CanValidateTokenCore(SecurityToken token)
        {
            return (token is CreditCardToken);
        }
    
        protected override ReadOnlyCollection<IAuthorizationPolicy> ValidateTokenCore(SecurityToken token)
        {
            CreditCardToken creditCardToken = token as CreditCardToken;
    
            if (creditCardToken.CardInfo.ExpirationDate < DateTime.UtcNow)
            {
                throw new SecurityTokenValidationException("The credit card has expired");
            }
            if (!IsCardNumberAndExpirationValid(creditCardToken.CardInfo))
            {
                throw new SecurityTokenValidationException("Unknown or invalid credit card");
            }
    
            // The credit card token has only 1 claim: the card number. The issuer for the claim is the
            // credit card issuer.
            DefaultClaimSet cardIssuerClaimSet = new DefaultClaimSet(new Claim(ClaimTypes.Name, creditCardToken.CardInfo.CardIssuer, Rights.PossessProperty));
            DefaultClaimSet cardClaimSet = new DefaultClaimSet(cardIssuerClaimSet, new Claim(Constants.CreditCardNumberClaim, creditCardToken.CardInfo.CardNumber, Rights.PossessProperty));
            List<IAuthorizationPolicy> policies = new List<IAuthorizationPolicy>(1);
            policies.Add(new CreditCardTokenAuthorizationPolicy(cardClaimSet));
            return policies.AsReadOnly();
        }
    
        // This helper method checks whether a given credit card entry is present in the user database.
        private bool IsCardNumberAndExpirationValid(CreditCardInfo cardInfo)
        {
            try
            {
                using (StreamReader myStreamReader = new StreamReader(this.creditCardsFile))
                {
                    string line = "";
                    while ((line = myStreamReader.ReadLine()) != null)
                    {
                        string[] splitEntry = line.Split('#');
                        if (splitEntry[0] == cardInfo.CardNumber)
                        {
                            string expirationDateString = splitEntry[1].Trim();
                            DateTime expirationDateOnFile = DateTime.Parse(expirationDateString, System.Globalization.DateTimeFormatInfo.InvariantInfo, System.Globalization.DateTimeStyles.AdjustToUniversal);
                            if (cardInfo.ExpirationDate == expirationDateOnFile)
                            {
                                string issuer = splitEntry[2];
                                return issuer.Equals(cardInfo.CardIssuer, StringComparison.InvariantCultureIgnoreCase);
                            }
                            else
                            {
                                return false;
                            }
                        }
                    }
                    return false;
                }
            }
            catch (Exception e)
            {
                throw new Exception("BookStoreService: Error while retrieving credit card information from User DB " + e.ToString());
            }
        }
    }
    
    Public Class CreditCardTokenAuthorizationPolicy
        Implements IAuthorizationPolicy
    
        Private _id As String
        Private _issuer As ClaimSet
        Private _issuedClaimSets As IEnumerable(Of ClaimSet)
    
        Public Sub New(ByVal issuedClaims As ClaimSet)
            If issuedClaims Is Nothing Then
                Throw New ArgumentNullException("issuedClaims")
            End If
            Me._issuer = issuedClaims.Issuer
            Me._issuedClaimSets = New ClaimSet() {issuedClaims}
            Me._id = Guid.NewGuid().ToString()
        End Sub
    
        Public ReadOnly Property Issuer() As ClaimSet Implements IAuthorizationPolicy.Issuer
            Get
                Return Me._issuer
            End Get
        End Property
    
        Public ReadOnly Property Id() As String Implements System.IdentityModel.Policy.IAuthorizationComponent.Id
            Get
                Return Me._id
            End Get
        End Property
    
        Public Function Evaluate(ByVal context As EvaluationContext, _
                                 ByRef state As Object) As Boolean Implements IAuthorizationPolicy.Evaluate
            For Each issuance In Me._issuedClaimSets
                context.AddClaimSet(Me, issuance)
            Next issuance
    
            Return True
        End Function
    
    End Class
    
    public class CreditCardTokenAuthorizationPolicy : IAuthorizationPolicy
    {
        string id;
        ClaimSet issuer;
        IEnumerable<ClaimSet> issuedClaimSets;
    
        public CreditCardTokenAuthorizationPolicy(ClaimSet issuedClaims)
        {
            if (issuedClaims == null)
                throw new ArgumentNullException("issuedClaims");
            this.issuer = issuedClaims.Issuer;
            this.issuedClaimSets = new ClaimSet[] { issuedClaims };
            this.id = Guid.NewGuid().ToString();
        }
    
        public ClaimSet Issuer { get { return this.issuer; } }
    
        public string Id { get { return this.id; } }
    
        public bool Evaluate(EvaluationContext context, ref object state)
        {
            foreach (ClaimSet issuance in this.issuedClaimSets)
            {
                context.AddClaimSet(this, issuance);
            }
    
            return true;
        }
    }
    

将自定义安全令牌与安全令牌管理器进行集成

  1. 安全令牌管理器可以创建相应的令牌提供程序、安全身份验证器和令牌序列化程序实例。若要创建自定义令牌管理器,请创建一个从 ClientCredentialsSecurityTokenManager 类继承的类。该类的主要方法使用 SecurityTokenRequirement 来创建相应的提供程序以及客户端或服务凭据。有关自定义安全令牌管理器的更多信息,请参见演练:创建自定义客户端和服务凭据

    Public Class CreditCardClientCredentialsSecurityTokenManager
        Inherits ClientCredentialsSecurityTokenManager
    
        Private creditCardClientCredentials As CreditCardClientCredentials
    
        Public Sub New(ByVal creditCardClientCredentials As CreditCardClientCredentials)
            MyBase.New(creditCardClientCredentials)
            Me.creditCardClientCredentials = creditCardClientCredentials
        End Sub
    
        Public Overrides Function CreateSecurityTokenProvider(ByVal tokenRequirement As SecurityTokenRequirement) As SecurityTokenProvider
    
            If tokenRequirement.TokenType = Constants.CreditCardTokenType Then
                ' Handle this token for Custom.
                Return New CreditCardTokenProvider(Me.creditCardClientCredentials.CreditCardInfo)
            ElseIf TypeOf tokenRequirement Is InitiatorServiceModelSecurityTokenRequirement Then
                ' Return server certificate.
                If tokenRequirement.TokenType = SecurityTokenTypes.X509Certificate Then
                    Return New X509SecurityTokenProvider(creditCardClientCredentials.ServiceCertificate.DefaultCertificate)
                End If
            End If
            Return MyBase.CreateSecurityTokenProvider(tokenRequirement)
        End Function
    
        Public Overloads Overrides Function CreateSecurityTokenSerializer(ByVal version As SecurityTokenVersion) As SecurityTokenSerializer
            Return New CreditCardSecurityTokenSerializer(version)
        End Function
    
    End Class
    
    public class CreditCardClientCredentialsSecurityTokenManager : ClientCredentialsSecurityTokenManager
    {
        CreditCardClientCredentials creditCardClientCredentials;
    
        public CreditCardClientCredentialsSecurityTokenManager(CreditCardClientCredentials creditCardClientCredentials)
            : base(creditCardClientCredentials)
        {
            this.creditCardClientCredentials = creditCardClientCredentials;
        }
    
        public override SecurityTokenProvider CreateSecurityTokenProvider(SecurityTokenRequirement tokenRequirement)
        {
            if (tokenRequirement.TokenType == Constants.CreditCardTokenType)
            {
                // Handle this token for Custom.
                return new CreditCardTokenProvider(this.creditCardClientCredentials.CreditCardInfo);
            }
            else if (tokenRequirement is InitiatorServiceModelSecurityTokenRequirement)
            {
                // Return server certificate.
                if (tokenRequirement.TokenType == SecurityTokenTypes.X509Certificate)
                {
                    return new X509SecurityTokenProvider(creditCardClientCredentials.ServiceCertificate.DefaultCertificate);
                }
            }
            return base.CreateSecurityTokenProvider(tokenRequirement);
        }
    
        public override SecurityTokenSerializer CreateSecurityTokenSerializer(SecurityTokenVersion version)
        {
            return new CreditCardSecurityTokenSerializer(version);
        }
    
    }
    
    Public Class CreditCardServiceCredentialsSecurityTokenManager
        Inherits ServiceCredentialsSecurityTokenManager
    
        Private creditCardServiceCredentials As CreditCardServiceCredentials
    
        Public Sub New(ByVal creditCardServiceCredentials As CreditCardServiceCredentials)
            MyBase.New(creditCardServiceCredentials)
            Me.creditCardServiceCredentials = creditCardServiceCredentials
        End Sub
    
        Public Overrides Function CreateSecurityTokenAuthenticator(ByVal tokenRequirement As SecurityTokenRequirement, _
                                                                   <System.Runtime.InteropServices.Out()> ByRef outOfBandTokenResolver As SecurityTokenResolver) As SecurityTokenAuthenticator
            If tokenRequirement.TokenType = Constants.CreditCardTokenType Then
                outOfBandTokenResolver = Nothing
                Return New CreditCardTokenAuthenticator(creditCardServiceCredentials.CreditCardDataFile)
            End If
            Return MyBase.CreateSecurityTokenAuthenticator(tokenRequirement, outOfBandTokenResolver)
        End Function
    
        Public Overrides Function CreateSecurityTokenSerializer(ByVal version As SecurityTokenVersion) As SecurityTokenSerializer
            Return New CreditCardSecurityTokenSerializer(version)
        End Function
    
    End Class
    
    public class CreditCardServiceCredentialsSecurityTokenManager : ServiceCredentialsSecurityTokenManager
    {
        CreditCardServiceCredentials creditCardServiceCredentials;
    
        public CreditCardServiceCredentialsSecurityTokenManager(CreditCardServiceCredentials creditCardServiceCredentials)
            : base(creditCardServiceCredentials)
        {
            this.creditCardServiceCredentials = creditCardServiceCredentials;
        }
    
        public override SecurityTokenAuthenticator CreateSecurityTokenAuthenticator(SecurityTokenRequirement tokenRequirement, out SecurityTokenResolver outOfBandTokenResolver)
        {
            if (tokenRequirement.TokenType == Constants.CreditCardTokenType)
            {
                outOfBandTokenResolver = null;
                return new CreditCardTokenAuthenticator(creditCardServiceCredentials.CreditCardDataFile);
            }
            return base.CreateSecurityTokenAuthenticator(tokenRequirement, out outOfBandTokenResolver);
        }
    
        public override SecurityTokenSerializer CreateSecurityTokenSerializer(SecurityTokenVersion version)
        {
            return new CreditCardSecurityTokenSerializer(version);
        }
    }
    

将自定义安全令牌与自定义客户端和服务凭据进行集成

  1. 必须添加自定义客户端和服务凭据,才能向应用程序提供一个 API,使其能够指定先前创建的自定义安全令牌基础结构所使用的自定义令牌信息,从而提供自定义安全令牌内容并对内容进行身份验证。下面的示例演示如何执行此操作。有关自定义客户端和服务凭据的更多信息,请参见演练:创建自定义客户端和服务凭据

    Public Class CreditCardClientCredentials
        Inherits ClientCredentials
    
        Private _creditCardInfo As CreditCardInfo
    
        Public Sub New(ByVal creditCardInfo As CreditCardInfo)
            MyBase.New()
            If creditCardInfo Is Nothing Then
                Throw New ArgumentNullException("creditCardInfo")
            End If
    
            Me._creditCardInfo = creditCardInfo
        End Sub
    
        Public ReadOnly Property CreditCardInfo() As CreditCardInfo
            Get
                Return Me._creditCardInfo
            End Get
        End Property
    
        Protected Overrides Function CloneCore() As ClientCredentials
            Return New CreditCardClientCredentials(Me._creditCardInfo)
        End Function
    
        Public Overrides Function CreateSecurityTokenManager() As SecurityTokenManager
            Return New CreditCardClientCredentialsSecurityTokenManager(Me)
        End Function
    
    End Class
    
    public class CreditCardClientCredentials : ClientCredentials
    {
        CreditCardInfo creditCardInfo;
    
        public CreditCardClientCredentials(CreditCardInfo creditCardInfo)
            : base()
        {
            if (creditCardInfo == null)
            {
                throw new ArgumentNullException("creditCardInfo");
            }
    
            this.creditCardInfo = creditCardInfo;
        }
    
        public CreditCardInfo CreditCardInfo
        {
            get { return this.creditCardInfo; }
        }
    
        protected override ClientCredentials CloneCore()
        {
            return new CreditCardClientCredentials(this.creditCardInfo);
        }
    
        public override SecurityTokenManager CreateSecurityTokenManager()
        {
            return new CreditCardClientCredentialsSecurityTokenManager(this);
        }
    }
    
    Public Class CreditCardServiceCredentials
        Inherits ServiceCredentials
    
        Private creditCardFile As String
    
        Public Sub New(ByVal creditCardFile As String)
            MyBase.New()
            If creditCardFile Is Nothing Then
                Throw New ArgumentNullException("creditCardFile")
            End If
    
            Me.creditCardFile = creditCardFile
        End Sub
    
        Public ReadOnly Property CreditCardDataFile() As String
            Get
                Return Me.creditCardFile
            End Get
        End Property
    
        Protected Overrides Function CloneCore() As ServiceCredentials
            Return New CreditCardServiceCredentials(Me.creditCardFile)
        End Function
    
        Public Overrides Function CreateSecurityTokenManager() As SecurityTokenManager
            Return New CreditCardServiceCredentialsSecurityTokenManager(Me)
        End Function
    
    End Class
    
    public class CreditCardServiceCredentials : ServiceCredentials
    {
        string creditCardFile;
    
        public CreditCardServiceCredentials(string creditCardFile)
            : base()
        {
            if (creditCardFile == null)
            {
                throw new ArgumentNullException("creditCardFile");
            }
    
            this.creditCardFile = creditCardFile;
        }
    
        public string CreditCardDataFile
        {
            get { return this.creditCardFile; }
        }
    
        protected override ServiceCredentials CloneCore()
        {
            return new CreditCardServiceCredentials(this.creditCardFile);
        }
    
        public override SecurityTokenManager CreateSecurityTokenManager()
        {
            return new CreditCardServiceCredentialsSecurityTokenManager(this);
        }
    }
    

先前创建的自定义安全令牌参数类用于通知 WCF 安全框架,在与服务通信时必须使用自定义安全令牌。下面的过程演示如何执行此操作。

将自定义安全令牌与绑定进行集成

  1. SecurityBindingElement 类上公开的一个令牌参数集合中,必须指定自定义安全令牌参数类。下面的示例使用 SignedEncrypted 所返回的集合。代码将信用卡自定义令牌添加到从客户端发送到服务的每个消息中,并对令牌内容自动进行了签名和加密。

    Public NotInheritable Class BindingHelper
    
        Private Sub New()
        End Sub
    
        Public Shared Function CreateCreditCardBinding() As Binding
            Dim httpTransport As New HttpTransportBindingElement()
    
            ' The message security binding element is configured to require a credit card
            ' token that is encrypted with the service's certificate. 
            Dim messageSecurity As New SymmetricSecurityBindingElement()
            messageSecurity.EndpointSupportingTokenParameters.SignedEncrypted.Add(New CreditCardTokenParameters())
            Dim x509ProtectionParameters As New X509SecurityTokenParameters()
            x509ProtectionParameters.InclusionMode = SecurityTokenInclusionMode.Never
            messageSecurity.ProtectionTokenParameters = x509ProtectionParameters
            Return New CustomBinding(messageSecurity, httpTransport)
        End Function
    
    End Class
    
    public static class BindingHelper
    {
        public static Binding CreateCreditCardBinding()
        {
            HttpTransportBindingElement httpTransport = new HttpTransportBindingElement();
    
            // The message security binding element is configured to require a credit card
            // token that is encrypted with the service's certificate. 
            SymmetricSecurityBindingElement messageSecurity = new SymmetricSecurityBindingElement();
            messageSecurity.EndpointSupportingTokenParameters.SignedEncrypted.Add(new CreditCardTokenParameters());
            X509SecurityTokenParameters x509ProtectionParameters = new X509SecurityTokenParameters();
            x509ProtectionParameters.InclusionMode = SecurityTokenInclusionMode.Never;
            messageSecurity.ProtectionTokenParameters = x509ProtectionParameters;
            return new CustomBinding(messageSecurity, httpTransport);
        }
    }
    

另请参见

任务

如何:创建自定义安全令牌提供程序

参考

SecurityToken
SecurityTokenParameters
WSSecurityTokenSerializer
SecurityTokenProvider
SecurityTokenAuthenticator
IAuthorizationPolicy
SecurityTokenRequirement
SecurityTokenManager
ClientCredentials
ServiceCredentials
SecurityBindingElement

概念

演练:创建自定义客户端和服务凭据
如何:创建自定义安全令牌身份验证器
安全体系结构