Share via


방법: 사용자 지정 토큰 만들기

이 항목에서는 SecurityToken 클래스를 사용하여 사용자 지정 보안 토큰을 만들고 사용자 지정 보안 토큰 공급자 및 인증자를 사용하여 통합하는 방법에 대해 설명합니다. 자세한 코드 예제는 사용자 지정 토큰 샘플을 참조하세요.

보안 토큰은 기본적으로 WCF(Windows Communication Foundation) 보안 프레임워크에서 SOAP 메시지 내부의 발신자에 대한 클레임을 나타내는 데 사용되는 XML 요소입니다. WCF 보안은 시스템에서 제공한 인증 모드에 대해 다양한 토큰을 제공합니다. 예를 들면 X509SecurityToken 클래스에 의해 표시되는 X.509 인증서 보안 토큰과 UserNameSecurityToken 클래스에 의해 표시되는 사용자 이름 보안 토큰이 있습니다.

제공된 형식이 인증 모드나 자격 증명을 지원하지 않는 경우도 있습니다. 그럴 경우 사용자 지정 보안 토큰을 만들어 SOAP 메시지에서 사용자 지정 자격 증명의 XML 표현을 제공해야 합니다.

다음 절차에서는 사용자 지정 보안 토큰을 만드는 방법과 이를 WCF 보안 인프라와 통합하는 방법에 대해 설명합니다. 이 항목에서는 클라이언트의 신용 카드 관련 정보를 서버에 전달하는 데 사용되는 신용 카드 토큰을 만듭니다.

사용자 지정 자격 증명 및 보안 토큰 관리자에 대한 자세한 내용은 연습: 사용자 지정 클라이언트 및 서비스 자격 증명 만들기를 참조하세요.

보안 토큰을 나타내는 클래스에 대한 자세한 내용은 System.IdentityModel.Tokens 네임스페이스를 참조하십시오.

절차

클라이언트 애플리케이션에 보안 인프라에 대한 신용 카드 정보를 지정할 수 있는 방법이 제공되어야 합니다. 이 정보는 사용자 지정 클라이언트 자격 증명 클래스를 통해 애플리케이션에서 사용할 수 있습니다. 첫 번째 단계에서는 사용자 지정 클라이언트 자격 증명에 대한 신용 카드 정보를 나타내는 클래스를 만듭니다.

클라이언트 자격 증명에서 신용 카드 정보를 나타내는 클래스를 만들려면

  1. 애플리케이션에 대한 신용 카드 정보를 나타내는 새 클래스를 정의합니다. 다음 예제에서는 클래스의 이름을 CreditCardInfo로 지정합니다.

  2. 애플리케이션에서 사용자 지정 토큰에 필요한 정보를 설정할 수 있도록 클래스에 적절한 속성을 추가합니다. 이 예제에서는 클래스에 CardNumber, CardIssuerExpirationDate의 세 속성이 있습니다.

    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
    

이제 사용자 지정 보안 토큰을 나타내는 클래스를 만들어야 합니다. 이 클래스는 보안 토큰 공급자, 인증자 및 serializer 클래스에서 WCF 보안 인프라와 보안 토큰 관련 정보를 주고 받는 데 사용합니다.

사용자 지정 보안 토큰 클래스를 만들려면

  1. SecurityToken 클래스에서 파생된 새 클래스를 정의합니다. 이 예제에서는 CreditCardToken 클래스를 만듭니다.

  2. Id 속성을 재정의합니다. 이 속성은 SOAP 메시지 내의 다른 요소에서 보안 토큰 XML 표현을 가리키는 데 사용되는 보안 토큰의 로컬 식별자를 가져올 때 사용합니다. 이 예제에서는 토큰 식별자를 생성자 매개 변수로 전달하거나 보안 토큰 인스턴스를 만들 때마다 새 토큰 식별자를 임의로 생성합니다.

  3. SecurityKeys 속성을 구현합니다. 이 속성은 보안 토큰 인스턴스가 나타내는 보안 키 컬렉션을 반환합니다. 이러한 키는 WCF에서 SOAP 메시지 부분을 서명하거나 암호화하는 데 사용할 수 있습니다. 이 예제에서 신용 카드 보안 토큰은 보안 키를 포함할 수 없기 때문에 속성 구현 시 항상 빈 컬렉션을 반환합니다.

  4. ValidFromValidTo 속성을 재정의합니다. 이러한 속성은 WCF에서 보안 토큰 인스턴스의 유효성을 확인하는 데 사용됩니다. 이 예제에서는 신용 카드 보안 토큰에 만료 날짜만 있으므로, ValidFrom 속성은 인스턴스를 만든 날짜와 시간을 나타내는 DateTime을 반환합니다.

    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
    

새 보안 토큰 형식을 만들 때 SecurityTokenParameters 클래스를 구현해야 합니다. 구현은 보안 바인딩 요소 구성에서 새 토큰 형식을 나타내는 데 사용됩니다. 보안 토큰 매개 변수 클래스는 메시지를 처리할 때 실제 보안 토큰 인스턴스를 일치시키는 데 사용되는 템플릿 역할을 합니다. 템플릿은 애플리케이션에서 사용 또는 인증을 위해 보안 토큰을 일치시켜야 하는 기준을 지정하는 데 사용할 수 있는 추가 속성을 제공합니다. 다음 예제에서는 속성을 추가하지 않으므로, WCF 인프라에서 사용하거나 유효성을 검사할 보안 토큰 인스턴스를 검색할 때 보안 토큰 형식만 일치시킵니다.

사용자 지정 보안 토큰 매개 변수 클래스를 만들려면

  1. SecurityTokenParameters 클래스에서 파생된 새 클래스를 정의합니다.

  2. CloneCore 메서드를 구현합니다. 클래스에 정의된 모든 내부 필드(있는 경우)를 복사합니다. 이 예제에서는 추가 필드를 정의하지 않습니다.

  3. SupportsClientAuthentication 읽기 전용 속성을 구현합니다. 이 속성은 이 클래스에 표시된 보안 토큰 형식을 사용하여 서비스에 클라이언트를 인증할 수 있는 경우 true를 반환합니다. 이 예제에서는 신용 카드 보안 토큰을 사용하여 서비스에 클라이언트를 인증할 수 있습니다.

  4. SupportsServerAuthentication 읽기 전용 속성을 구현합니다. 이 속성은 이 클래스에 표시된 보안 토큰 형식을 사용하여 클라이언트에 서비스를 인증할 수 있는 경우 true를 반환합니다. 이 예제에서는 신용 카드 보안 토큰을 사용하여 클라이언트에 서비스를 인증할 수 없습니다.

  5. SupportsClientWindowsIdentity 읽기 전용 속성을 구현합니다. 이 속성은 이 클래스에 표시된 보안 토큰 형식을 Windows 계정에 매핑할 수 있는 경우 true를 반환합니다. 그럴 경우 인증 결과는 WindowsIdentity 클래스 인스턴스에 의해 표시됩니다. 이 예제에서는 토큰을 Windows 계정에 매핑할 수 없습니다.

  6. CreateKeyIdentifierClause(SecurityToken, SecurityTokenReferenceStyle) 메서드를 구현합니다. 이 보안 토큰 매개 변수 클래스에 표시되는 보안 토큰 인스턴스에 대한 참조가 필요한 경우 WCF 보안 프레임워크에 의해 이 메서드가 호출됩니다. 실제 보안 토큰 인스턴스 및 요청 중인 참조 형식을 지정하는 SecurityTokenReferenceStyle이 모두 이 메서드에 인수로 전달됩니다. 이 예제에서 신용 카드 보안 토큰은 내부 참조만 지원합니다. SecurityToken 클래스에는 내부 참조를 만드는 기능이 있으므로 구현 시에 추가 코드가 필요하지 않습니다.

  7. InitializeSecurityTokenRequirement(SecurityTokenRequirement) 메서드를 구현합니다. 이 메서드는 WCF에서 호출하여 보안 토큰 매개 변수 클래스 인스턴스를 SecurityTokenRequirement 클래스의 인스턴스로 변환합니다. 결과는 보안 토큰 공급자가 해당 보안 토큰 인스턴스를 만드는 데 사용됩니다.

    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
    

보안 토큰은 SOAP 메시지 내부에서 전송되며, 메모리 내 보안 토큰 표시와 통신 중 표현 간의 변환 메커니즘이 필요합니다. WCF에서는 보안 토큰 serializer를 사용하여 이 작업을 수행합니다. 모든 사용자 지정 토큰은 SOAP 메시지로부터 사용자 지정 보안 토큰을 직렬화 및 역직렬화할 수 있는 사용자 지정 보안 토큰 serializer와 함께 표시되어야 합니다.

참고 항목

파생 키는 기본적으로 활성화됩니다. 사용자 지정 보안 토큰을 만들고 주 토큰으로 사용하면 WCF에서 해당 토큰에서 키를 파생시킵니다. 이때 사용자 지정 보안 토큰 serializer를 호출하여 SecurityKeyIdentifierClause을 네트워크에 serialize하는 동안 사용자 지정 보안 토큰에 대해 DerivedKeyToken을 작성합니다. 받는 측에서는 연결이 끊어진 상태에서 토큰을 역직렬화하면 DerivedKeyToken 직렬 변환기에서 SecurityTokenReference 요소를 자체의 최상위 자식으로 예상합니다. 사용자 지정 보안 토큰 serializer가 해당 절 형식을 serialize하는 동안 SecurityTokenReference 요소를 추가하지 않으면 예외가 throw됩니다.

사용자 지정 보안 토큰 serializer를 만들려면

  1. WSSecurityTokenSerializer 클래스에서 파생된 새 클래스를 정의합니다.

  2. CanReadTokenCore(XmlReader)를 사용하여 XML 스트림을 읽도록 XmlReader 메서드를 재정의합니다. 이 메서드는 직렬 변환기 구현에서 지정된 현재 요소를 기반으로 보안 토큰을 역직렬화할 수 있는 경우 true를 반환합니다. 이 예제에서 이 메서드는 XML 판독기의 현재 XML 요소의 요소 이름과 네임스페이스가 올바른지 확인합니다. 올바르지 않은 경우 이 메서드의 기본 클래스 구현을 호출하여 XML 요소를 처리합니다.

  3. ReadTokenCore(XmlReader, SecurityTokenResolver) 메서드를 재정의합니다. 이 메서드는 보안 토큰의 XML 내용을 읽고 적절한 메모리 내 표현을 생성합니다. 전달된 XML 판독기에 표시된 XML 요소를 인식할 수 없으면 기본 클래스 구현을 호출하여 시스템 제공 토큰 형식을 처리합니다.

  4. CanWriteTokenCore(SecurityToken) 메서드를 재정의합니다. 이 메서드는 인수로 전달된 메모리 내 토큰 표현을 XML 표현으로 변환할 수 있으면 true를 반환합니다. 변환할 수 없으면 기본 클래스 구현을 호출합니다.

  5. WriteTokenCore(XmlWriter, SecurityToken) 메서드를 재정의합니다. 이 메서드는 메모리 내 보안 토큰 표현을 XML 표현으로 변환합니다. 변환할 수 없으면 기본 클래스 구현을 호출합니다.

    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
    

앞의 네 가지 절차를 완료한 후 보안 토큰 공급자, 인증자, 관리자, 클라이언트 및 서비스 자격 증명에 사용자 지정 보안 토큰을 통합합니다.

사용자 지정 보안 토큰을 보안 토큰 공급자와 통합하려면

  1. 보안 토큰 공급자는 토큰 인스턴스를 작성, 수정(필요한 경우) 및 반환합니다. 사용자 지정 보안 토큰에 대한 사용자 지정 공급자를 만들려면 SecurityTokenProvider 클래스에서 상속되는 클래스를 만듭니다. 다음 예제에서는 GetTokenCore 인스턴스를 반환하도록 CreditCardToken 메서드를 재정의합니다. 사용자 지정 보안 토큰 공급자에 대한 자세한 내용은 방법: 사용자 지정 보안 토큰 공급자 만들기를 참조하세요.

    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
    

사용자 지정 보안 토큰을 보안 토큰 인증자와 통합하려면

  1. 보안 토큰 인증자는 메시지에서 추출된 보안 토큰 내용의 유효성을 검사합니다. 사용자 지정 보안 토큰에 대한 사용자 지정 인증자를 만들려면 SecurityTokenAuthenticator 클래스에서 상속되는 클래스를 만듭니다. 다음 예에서는 ValidateTokenCore 메서드를 재정의합니다. 사용자 지정 보안 토큰 인증자에 대한 자세한 내용은 방법: 사용자 지정 보안 토큰 인증자 만들기를 참조하세요.

    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
    

사용자 지정 보안 토큰을 보안 토큰 관리자와 통합하려면

  1. 보안 토큰 관리자는 적절한 토큰 공급자, 보안 인증자 및 토큰 serializer 인스턴스를 만듭니다. 사용자 지정 토큰 관리자를 만들려면 ClientCredentialsSecurityTokenManager에서 상속되는 클래스를 만듭니다. 클래스의 기본 메서드에서는 SecurityTokenRequirement를 사용하여 적절한 공급자와 클라이언트 또는 서비스 자격 증명을 만듭니다. 사용자 지정 보안 토큰 관리자에 대한 자세한 내용은 연습: 사용자 지정 클라이언트 및 서비스 자격 증명 만들기를 참조하세요.

    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
    

사용자 지정 보안 토큰을 사용자 지정 클라이언트 및 서비스 자격 증명과 통합하려면

  1. 애플리케이션에서 사용자 지정 보안 토큰 내용을 제공 및 인증하기 위해 앞에서 만든 사용자 지정 보안 토큰 인프라에 사용되는 사용자 지정 토큰 정보를 지정할 수 있도록 API를 제공하려면 사용자 지정 클라이언트 및 서비스 자격 증명을 추가해야 합니다. 다음 샘플은 이 작업을 수행하는 방법을 보여 줍니다. 사용자 지정 클라이언트 및 서비스 자격 증명에 대한 자세한 내용은 연습: 사용자 지정 클라이언트 및 서비스 자격 증명 만들기를 참조하세요.

    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
    

앞에서 만든 사용자 지정 보안 토큰 매개 변수 클래스는 WCF 보안 프레임워크에 서비스와 통신할 때 사용자 지정 보안 토큰을 사용하도록 지시하는 데 사용됩니다. 다음 절차는 이 작업을 수행하는 방법을 보여 줍니다.

사용자 지정 보안 토큰을 바인딩과 통합하려면

  1. SecurityBindingElement 클래스에 노출되는 토큰 매개 변수 컬렉션 중 하나에서 사용자 지정 보안 토큰 매개 변수 클래스를 지정해야 합니다. 다음 예제에서는 SignedEncrypted가 반환하는 컬렉션을 사용합니다. 이 코드는 내용을 자동으로 서명한 후 암호화하여 클라이언트에서 서비스로 보낸 모든 메시지에 신용 카드 사용자 지정 토큰을 추가합니다.

    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
    

이 항목에서는 사용자 지정 토큰을 구현하고 사용하는 데 필요한 다양한 코드를 보여 줍니다. 이러한 코드가 서로 어떻게 연결되는지에 대한 전체 예제를 보려면 사용자 지정 토큰을 참조하세요.

참고 항목