語言
本主題將說明如何使用該 SecurityToken 類別建立自訂安全令牌,以及如何將其整合至自訂安全令牌提供者與認證器。 完整程式碼範例請參見 自訂代幣 範例。
安全權杖本質上是一個 XML 元素,由 Windows 通訊基金會(WCF)安全框架用來表示 SOAP 訊息中關於發送者的主張。 WCF 安全性提供多種系統提供的認證模式令牌。 例如,X.509 憑證安全令牌由 X509SecurityToken 類別代表,或使用者名稱安全令牌由類別代表 UserNameSecurityToken 。
有時所提供的類型不支援認證模式或憑證。 此時,必須建立自訂安全權杖,以在 SOAP 訊息中提供自訂憑證的 XML 表示。
以下程序說明如何建立自訂安全令牌,以及如何將其整合至WCF安全基礎設施。 此主題建立一個信用卡令牌,用以傳遞客戶端信用卡資訊給伺服器。
欲了解更多關於自訂憑證與安全憑證管理器的資訊,請參閱「 攻略:建立自訂客戶端與服務憑證」。
請參閱 System.IdentityModel.Tokens 命名空間以了解更多代表安全權杖的類別。
程序
用戶端應用程式必須提供一種方式,以指定安全基礎設施所需的信用卡資訊。 這些資訊會透過自訂的用戶端憑證類別提供給應用程式。 第一步是建立一個類別來代表信用卡資訊,用於自訂客戶憑證。
建立一個類別,代表客戶憑證中的信用卡資訊
定義一個新的類別,代表應用程式中的信用卡資訊。 以下範例命名為類別
CreditCardInfo。為類別加入適當的屬性,讓應用程式能設定自訂標記所需的資訊。 在此範例中,該類別具有三個性質:
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
接著,必須建立一個代表自訂安全權杖的類別。 此類別由安全令牌提供者、認證器及序列化器類別用來傳遞安全令牌資訊,與 WCF 安全基礎設施之間交流。
建立自訂安全令牌類別
定義衍生自 SecurityToken 類別的新類別。 此範例會產生一個名為
CreditCardToken的類別。覆蓋該 Id 屬性。 此屬性用於取得安全權杖的本地識別碼,該識別碼用於指向 SOAP 訊息中其他元素的安全權杖 XML 表示。 在此範例中,標記識別碼可以作為建構參數傳遞給它,或每次建立安全憑證實例時產生一個新的隨機識別碼。
請實作 SecurityKeys 屬性。 此屬性回傳一組安全金鑰,該安全權杖實例代表這些金鑰。 WCF 可利用此類金鑰簽署或加密 SOAP 訊息的部分內容。 在此範例中,信用卡安全令牌不能包含任何安全金鑰;因此,實作總是回傳一個空集合。
覆蓋 ValidFrom 和 ValidTo 屬性。 這些屬性被 WCF 用來判定安全憑證實例的有效性。 在這個例子中,信用卡安全令牌只有一個有效期限,因此屬性
ValidFrom回傳的 a 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 基礎架構搜尋安全憑證實例以使用或驗證時,僅匹配安全權杖類型。
建立自訂的安全憑證參數類別
定義衍生自 SecurityTokenParameters 類別的新類別。
實作 CloneCore 方法。 如果有,請複製你類別中所有定義的內部欄位。 此範例未定義任何額外欄位。
實作 SupportsClientAuthentication 唯讀特性。 若該類別所代表的安全憑證類型可用來驗證客戶端至服務,則此屬性會回傳
true。 在此範例中,信用卡安全令牌可以用來將客戶端的身份驗證給服務。實作 SupportsServerAuthentication 唯讀特性。 若此類別所代表的安全憑證類型可用於驗證服務給用戶端,則此屬性會回傳
true。 在此範例中,信用卡安全令牌無法用來驗證服務給客戶端。實作 SupportsClientWindowsIdentity 唯讀特性。 若此類別所代表的安全權杖類型能映射到 Windows 帳號,則此屬性會回傳
true。 若是如此,認證結果會以 WindowsIdentity 類別實例表示。 在此範例中,該令牌無法映射到 Windows 帳號。實作 CreateKeyIdentifierClause(SecurityToken, SecurityTokenReferenceStyle) 方法。 當 WCF 安全框架需要參考由此安全權杖參數類別所代表的安全權杖實例時,會呼叫此方法。 實際的安全權杖實例以及指定被請求參考類型的SecurityTokenReferenceStyle,都會作為參數傳遞給此方法。 在此範例中,信用卡安全令牌僅支援內部引用。 該 SecurityToken 類別具備建立內部參考的功能;因此,實作不需要額外的程式碼。
實作 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 使用安全令牌序列化器來完成此任務。 每個自訂權杖必須附帶一個自訂安全權杖序列化器,該程式能將自訂安全權杖從 SOAP 訊息中序列化與反序列化。
備註
衍生金鑰預設啟用。 如果你建立自訂的安全令牌並用作主要令牌,WCF 會從中衍生出一把金鑰。 在此過程中,它會呼叫自訂安全令牌序列化器來寫入 SecurityKeyIdentifierClause 自訂安全令牌,同時序列化 到 DerivedKeyToken 線路。 在接收端,當從線路反序列化令牌時, DerivedKeyToken 串列器預期有元素 SecurityTokenReference 作為自身底層的子節點。 若自訂安全令牌序列化器在序列化子句類型時未新增 SecurityTokenReference 元素,則會拋出例外。
建立自訂的安全令牌序列化器
定義衍生自 WSSecurityTokenSerializer 類別的新類別。
覆寫 CanReadTokenCore(XmlReader) 方法,此方法依賴 XmlReader 來讀取 XML 串流。 如果序列化器實作能根據目前元素反序列化安全權杖,該方法會回傳
true。 在此範例中,此方法檢查 XML 讀取器目前的 XML 元素是否擁有正確的元素名稱與命名空間。 若不符合,則呼叫此方法的基底類別實作來處理 XML 元素。覆寫 ReadTokenCore(XmlReader, SecurityTokenResolver) 方法。 此方法讀取安全令牌的 XML 內容,並構建適當的記憶體表示方式。 若無法辨識傳入 XML 讀取器所站立的 XML 元素,則呼叫基底類別實作來處理系統提供的權杖類型。
覆寫 CanWriteTokenCore(SecurityToken) 方法。 此方法若能將記憶體中的標記表示(以參數形式傳遞)轉換為 XML 表示法,則回傳
true。 如果無法轉換,則呼叫基底類別實作。覆寫 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
完成前四個程序後,將自訂安全權杖與安全權杖提供者、驗證器、管理者,以及客戶端與服務憑證整合。
將自訂安全令牌與安全令牌提供者整合
安全憑證提供者會建立、修改(如有需要),並回傳該憑證的實例。 要建立自訂安全憑證的自訂提供者,請建立一個繼承該 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
將自訂安全令牌與安全令牌驗證器整合
安全權杖驗證器會在從訊息中擷取安全權杖內容時進行驗證。 要為自訂安全權杖建立自訂認證器,請建立一個繼承自該 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 Classpublic 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
將自訂安全令牌與安全令牌管理器整合
安全權杖管理器會建立適當的權杖提供者、安全驗證器及權杖序列化器實例。 要建立自訂的 token 管理器,請建立一個繼承自該 ClientCredentialsSecurityTokenManager 類別的類別。 該類別的主要方法使用 a 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 Classpublic 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
整合自訂安全令牌與自訂客戶端及服務憑證
必須新增自訂客戶端與服務憑證,以提供應用程式 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 Classpublic 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 安全框架,與服務通訊時必須使用自訂的安全權杖。 以下程序說明如何實現此目標。
將自訂安全令牌與繫結整合
自訂安全令牌參數類別必須在類別 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
本主題展示實作及使用自訂令牌所需的各種程式碼。 想完整示範這些程式碼如何相互配合, 請參考「自訂代幣」。