Procedura: creare un token personalizzato

Questo argomento illustra come creare un token di sicurezza personalizzato utilizzando la classe SecurityToken e come integrarlo in un provider e un autenticatore di token di sicurezza personalizzati. Per un esempio di codice completo, vedere l'esempio Token personalizzato.

Un token di sicurezza è essenzialmente un elemento XML utilizzato dal framework di sicurezza di Windows Communication Foundation (WCF) per rappresentare le attestazioni relative a un mittente nel messaggio SOAP. La sicurezza di WCF offre vari token per le modalità di autenticazione fornite dal sistema. Alcuni esempi sono il token di sicurezza basato su certificato X.509 rappresentato dalla classe X509SecurityToken o il token di sicurezza del nome utente rappresentato dalla classe UserNameSecurityToken.

Talvolta una determinata modalità di autenticazione o credenziale non è supportata dai tipi di token disponibili. In tal caso è necessario creare un token di sicurezza personalizzato per fornire una rappresentazione XML della credenziale personalizzata nel messaggio SOAP.

Le procedure seguenti illustrano come creare un token di sicurezza personalizzato e come integrarlo nell'infrastruttura di sicurezza di WCF. In particolare, questo argomento illustra la creazione di un token di carta di credito utilizzato per passare al server le informazioni della carta di credito del client.

Per altre informazioni sulle credenziali e sui gestori di token di sicurezza personalizzati, vedere Procedura dettagliata: Creazione di credenziali client e servizio personalizzate.

Per un elenco di altre classi utilizzate per rappresentare token di sicurezza, vedere lo spazio dei nomi System.IdentityModel.Tokens.

Procedure

È necessario fornire a un'applicazione client un modo per specificare le informazioni sulla carta di credito per l'infrastruttura di sicurezza. Per consentire all'applicazione di specificare queste informazioni viene utilizzata una classe di credenziali client personalizzata. Il primo passaggio consiste nel creare una classe che rappresenta le informazioni di carta di credito come credenziali client personalizzate.

Per creare una classe che rappresenta informazioni di carta di credito come credenziali client

  1. Definire una nuova classe che rappresenta le informazioni di carta di credito dell'applicazione. Nell'esempio seguente tale classe viene denominata CreditCardInfo.

  2. Aggiungere alla classe le proprietà appropriate per consentire a un'applicazione di impostare le informazioni necessarie per il token personalizzato. In questo esempio la classe presenta tre proprietà: CardNumber, CardIssuer e ExpirationDate.

    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; }
        }
    }
    
    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
    

È quindi necessario creare una classe che rappresenti il token di sicurezza personalizzato. Questa classe viene utilizzata dalle classi del serializzatore, dell'autenticatore e del provider del token di sicurezza per scambiare informazioni sul token di sicurezza con l'infrastruttura di sicurezza WCF.

Per creare una classe di token di sicurezza personalizzato

  1. Definire una nuova classe derivata dalla classe SecurityToken. In questo esempio viene creata una classe denominata CreditCardToken.

  2. Eseguire l'override della proprietà Id. Questa proprietà viene utilizzata per ottenere l'identificatore locale del token di sicurezza utilizzato dagli altri elementi contenuti nel messaggio SOAP per puntare alla rappresentazione XML del token di sicurezza. In questo esempio è possibile passare ad esso un identificatore del token come un parametro di costruttore oppure generarne uno casuale ogni volta che viene creata un'istanza del token di sicurezza.

  3. Implementare la proprietà SecurityKeys. Questa proprietà restituisce una raccolta di chiavi di sicurezza rappresentate dall'istanza del token di sicurezza. Tali chiavi possono essere utilizzate da WCF per firmare o crittografare parti del messaggio SOAP. In questo esempio, il token di sicurezza della carta di credito non può contenere chiavi di sicurezza. Pertanto, l'implementazione restituisce sempre una raccolta vuota.

  4. Eseguire l'override delle proprietà ValidFrom e ValidTo. Queste proprietà vengono utilizzate da WCF per determinare la validità dell'istanza del token di sicurezza. In questo esempio, il token di sicurezza della carta di credito presenta solo una data di scadenza ("ValidTo"), pertanto la proprietà ValidFrom restituisce un oggetto DateTime che rappresenta la data e l'ora di creazione dell'istanza.

    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; }
        }
    }
    
    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
    

Quando si crea un nuovo tipo di token di sicurezza è necessario implementare per tale tipo la classe SecurityTokenParameters. Questa implementazione viene utilizzata nella configurazione dell'elemento di associazione di sicurezza per rappresentare il nuovo tipo di token. La classe dei parametri del token di sicurezza rappresenta il modello rispetto a cui confrontare l'istanza effettiva del token di sicurezza quando viene elaborato un messaggio. Nel modello vengono fornite proprietà aggiuntive che un'applicazione può utilizzare per specificare i criteri che il token di sicurezza deve soddisfare per essere utilizzato o autenticato. Nell'esempio seguente non vengono aggiunte altre proprietà, di conseguenza la corrispondenza viene creata solo con il tipo di token di sicurezza quando l'infrastruttura WCF cerca un'istanza del token di sicurezza da utilizzare o convalidare.

Per creare una classe di parametri del token di sicurezza personalizzato

  1. Definire una nuova classe derivata dalla classe SecurityTokenParameters.

  2. Implementa il metodo CloneCore. Copiare tutti i campi interni definiti nella classe, se presenti. In questo esempio non viene definito alcun campo aggiuntivo.

  3. Implementare la proprietà di sola lettura SupportsClientAuthentication. Questa proprietà restituisce true se il tipo di token di sicurezza rappresentato da questa classe può essere utilizzato per autenticare un client presso un servizio. In questo esempio, il token di sicurezza della carta di credito può essere utilizzato a tale scopo.

  4. Implementare la proprietà di sola lettura SupportsServerAuthentication. Questa proprietà restituisce true se il tipo di token di sicurezza rappresentato da questa classe può essere utilizzato per autenticare un servizio presso un client. In questo esempio, il token di sicurezza della carta di credito non può essere utilizzato a tale scopo.

  5. Implementare la proprietà di sola lettura SupportsClientWindowsIdentity. Questa proprietà restituisce true se il tipo di token di sicurezza rappresentato da questa classe può essere associato a un account Windows. In questo caso, il risultato dell'autenticazione è rappresentato da un'istanza della classe WindowsIdentity. In questo esempio, il token non può essere associato a un account Windows.

  6. Implementa il metodo CreateKeyIdentifierClause(SecurityToken, SecurityTokenReferenceStyle). Questo metodo viene chiamato dal framework di sicurezza di WCF quando quest'ultimo richiede un riferimento all'istanza del token di sicurezza rappresentato da questa classe di parametri del token di sicurezza. Sia l'istanza del token di sicurezza effettivo sia lo stile SecurityTokenReferenceStyle che specifica il tipo del riferimento richiesto vengono passati a questo metodo come argomenti. In questo esempio, il token di sicurezza della carta di credito supporta soltanto riferimenti interni. Poiché la classe SecurityToken offre funzionalità in grado di creare riferimenti interni, l'implementazione non richiede codice aggiuntivo.

  7. Implementa il metodo InitializeSecurityTokenRequirement(SecurityTokenRequirement). Questo metodo viene chiamato da WCF per convertire l'istanza della classe di parametri del token di sicurezza in un'istanza della classe SecurityTokenRequirement. Il risultato della conversione viene utilizzato dai provider di token di sicurezza per creare l'istanza appropriata del token di sicurezza.

    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");
            }
        }
    }
    
    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
    

I token di sicurezza vengono trasmessi all'interno di messaggi SOAP. Ciò richiede un meccanismo di conversione fra la rappresentazione dei token di sicurezza in memoria e la rappresentazione dei token di sicurezza trasmessi. A tale scopo, WCF utilizza un serializzatore di token di sicurezza. A ogni token personalizzato è necessario associare un serializzatore di token di sicurezza personalizzato in grado di serializzare il token di sicurezza personalizzato nel messaggio SOAP nonché di eseguire l'operazione inversa.

Nota

Le chiavi derivate sono attivate per impostazione predefinita. Se si crea un token di sicurezza personalizzato e lo si utilizza come token primario, WCF ne deriva una chiave. Durante questa operazione, viene chiamato il serializzatore del token di sicurezza per scrivere SecurityKeyIdentifierClause per il token di sicurezza personalizzato durante la serializzazione di DerivedKeyToken in rete. Sul lato ricevente, durante la deserializzazione del token all'esterno della rete, il serializzatore DerivedKeyToken prevede sotto di sé un elemento SecurityTokenReference come elemento figlio di livello superiore. Se il serializzatore del token di sicurezza personalizzato non ha aggiunto un elemento SecurityTokenReference durante la serializzazione del relativo tipo di clausola, viene generata un'eccezione.

Per creare un serializzatore di token di sicurezza personalizzato

  1. Definire una nuova classe derivata dalla classe WSSecurityTokenSerializer.

  2. Eseguire l'override del metodo CanReadTokenCore(XmlReader) che si basa su un oggetto XmlReader per leggere il flusso XML. Il metodo restituisce true se l'implementazione del serializzatore può deserializzare il token di sicurezza in base all'elemento corrente del lettore. In questo esempio, il metodo verifica se il nome e lo spazio dei nomi dell'elemento XML corrente del lettore XML sono corretti. In caso contrario, viene chiamata l'implementazione della classe base di questo metodo per la gestione dell'elemento XML.

  3. Eseguire l'override del metodo ReadTokenCore(XmlReader, SecurityTokenResolver). Tramite questo metodo viene letto il contenuto XML del token di sicurezza e viene creata la rappresentazione in memoria appropriata di tale token. Se non riconosce l'elemento XML correntemente esaminato dal lettore XML specificato, tale metodo chiama l'implementazione della classe di base per elaborare i tipi di token forniti dal sistema.

  4. Eseguire l'override del metodo CanWriteTokenCore(SecurityToken). Questo metodo restituisce true se può convertire la rappresentazione in memoria del token (passata come argomento) nella rappresentazione XML. In caso contrario, tale metodo chiama l'implementazione della classe di base.

  5. Eseguire l'override del metodo WriteTokenCore(XmlWriter, SecurityToken). Questo metodo converte una rappresentazione in memoria del token di sicurezza in una rappresentazione XML. Qualora questa conversione risultasse impossibile, tale metodo chiama l'implementazione della classe di base.

    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);
            }
        }
    }
    
    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
    

Dopo aver completato le quattro procedure precedenti, integrare il token di sicurezza personalizzato nel provider, nell'autenticatore e nel gestore di token di sicurezza nonché nelle credenziali client e server.

Per integrare il token di sicurezza personalizzato in un provider di token di sicurezza

  1. Il provider del token di sicurezza crea, modifica (se necessario) e restituisce un'istanza del token. Per creare un provider personalizzato per il token di sicurezza personalizzato, creare una classe che eredita dalla classe SecurityTokenProvider. Nell'esempio seguente viene eseguito l'override del metodoGetTokenCore per restituire un'istanza del token CreditCardToken. Per altre informazioni sui provider di token di sicurezza personalizzati, vedere Procedura: Creare un provider di token di sicurezza personalizzati.

    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;
        }
    }
    
    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
    

Per integrare il token di sicurezza personalizzato in un autenticatore del token di sicurezza

  1. L'autenticatore del token di sicurezza convalida il contenuto del token di sicurezza quando quest'ultimo viene estratto dal messaggio. Per creare un autenticatore personalizzato per il token di sicurezza personalizzato, creare una classe che eredita dalla classe SecurityTokenAuthenticator. Nell'esempio seguente il metodo ValidateTokenCore viene sottoposto a override. Per altre informazioni sugli autenticatori del token di sicurezza personalizzati, vedere Procedura: Creare un autenticatore del token di sicurezza personalizzato.

    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());
            }
        }
    }
    
    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
    
    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;
        }
    }
    
    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
    

Per integrare il token di sicurezza personalizzato in un gestore del token di sicurezza

  1. Il gestore del token di sicurezza crea le istanze del provider di token, dell'autenticatore di sicurezza e del serializzatore di token appropriati. Per creare un gestore del token personalizzato, creare una classe che eredita dalla classe ClientCredentialsSecurityTokenManager. I metodi primari della classe utilizzano un oggetto SecurityTokenRequirement per creare le credenziali appropriate per il provider e il client o il servizio. Per altre informazioni sui gestori di token di sicurezza personalizzati, vedere Procedura dettagliata: Creazione di credenziali client e servizio personalizzate.

    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 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 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);
        }
    }
    
    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
    

Per integrare il token di sicurezza personalizzato in credenziali client e server personalizzate

  1. È necessario aggiungere le credenziali del client e del servizio personalizzate per fornire un'API che consenta all'applicazione di specificare informazioni sul token personalizzato che l'infrastruttura dei token di sicurezza personalizzati creata in precedenza utilizza per fornire e autenticare il contenuto del token di sicurezza personalizzato. Negli esempi seguenti viene illustrato come effettuare questa operazione. Per altre informazioni sulle credenziali del client e del servizio personalizzate, vedere Procedura dettagliata: Creazione di credenziali client e servizio personalizzate.

    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 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 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);
        }
    }
    
    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
    

La classe dei parametri del token di sicurezza personalizzato creata in precedenza viene utilizzata per specificare nel framework di sicurezza WCF la necessità di utilizzare un token di sicurezza personalizzato quando si comunica con un servizio. La procedura seguente illustra come eseguire questa operazione.

Per integrare il token di sicurezza personalizzato nell'associazione

  1. La classe dei parametri del token di sicurezza personalizzato deve essere specificata in una delle raccolte di parametri di token esposte nella classe SecurityBindingElement. Nell'esempio seguente viene utilizzata la raccolta restituita dall'elemento SignedEncrypted. Dopo aver crittografato e firmato automaticamente il contenuto del token personalizzato della carta di credito, il codice aggiunge quest'ultimo a ogni messaggio inviato dal client al servizio.

    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);
        }
    }
    
    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
    

In questo argomento vengono illustrate le varie parti di codice necessarie per implementare e utilizzare un token personalizzato. Per un esempio completo di tutte queste parti, vedere Token personalizzato.

Vedi anche