程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 使用Geneva框架構建安全令牌服務(STS)

使用Geneva框架構建安全令牌服務(STS)

編輯:關於.NET

本文基於 "Geneva" 框架的預發布版本撰寫而成。所有信息均有可能發生變更。

本文將介紹以下內容:

使用 Geneva 框架實現安全令牌服務

聯合安全性

聲明轉換

本文使用了以下技術:

Windows Communication Foundation、ASP.NET、Geneva 框架

Microsoft 基於聲明的訪問 (CBA) 平台戰略(代號為 "Geneva")包括 "Geneva" 框架、"Geneva" 服務器和 Windows CardSpace "Geneva"。Geneva 框架可為開發人員提供相關工具來構建基於聲明的應用程序和服務(這些服務包括由“安全令牌服務”(STS) 所頒發的令牌),還提供相關工具來構建自定義 STS 和啟用 CardSpace 的 Windows 應用程序。雖然 Geneva 服務器是企業級 STS,但 Geneva 框架可以為不需要企業級功能的環境構建自定義 STS。Windows CardSpace Geneva 由 Windows 客戶端計算機中作為標識選擇器和標識提供者的 Windows CardSpace 演變而來。

在我上一篇介紹 Geneva 框架的文章中,我曾討論過依靠 STS 頒發的令牌來構建基於聲明的 Windows Communication Foundation (WCF) 服務的一種較好方法。在這裡,我將使用 Geneva 框架來構建自定義 STS。

在繼續閱讀本文之前,建議您先閱讀一下由 Keith Brown 和 Sesha Mani 合著的Geneva 框架開發人員白皮書和我的上一篇文章“Geneva 框架構建基於聲明的 WCF 服務的更好方法”。

安全令牌服務入門

無論是基於 Geneva 服務器的 STS 還是使用 Geneva 框架構建的 STS,其主要作用都是作為安全網關對調用方進行身份驗證,並頒發附帶描述調用方聲明的安全令牌。從前面提到的文章中,您應該能夠回想起 STS 身份驗證支持多種方案:

從身份驗證機制中分離出應用程序和服務,從而使其能夠專注於授權相關聲明。

支持多種憑據類型,而不會使應用程序和服務的實現變復雜。

支持聯合方案,用戶可以通過在自身的域中進行身份驗證來獲得對另一個域中資源的訪問權限(通過建立不同域的 STS 之間的信任關系)。

簡化標識委派方案,經過身份驗證的用戶將獲得對下游服務的訪問權限。

簡化聲明轉換,使相關聲明能夠用於對應用程序和服務進行授權。

其中的任何方案都可以基於被動聯合(基於浏覽器)或主動聯合(基於 Windows 客戶端)。下面我將詳細介紹這些方案,同時說明如何使用 Geneva 框架在自定義 STS 中構建相關邏輯。

在深入探討 STS 的實現之前,讓我們先來回顧一些基礎知識。在主動聯合中使用的 STS 是 WS-Federation 主動請求者配置文件(請參閱 WS-Federation TC)和(主要)WS-Trust 規范(請參閱 WS-Trust 1.3)的實現。

從較高層次看,WS-Trust 使用四種服務操作來描述一個約定:頒發、驗證、續訂和取消。客戶端分別調用這些操作來請求安全令牌、驗證安全令牌、續訂已過期的安全令牌以及取消不應再繼續使用的安全令牌。根據 WS-Trust 規范,每個操作都必須以“請求安全令牌”(RST) 的格式發送消息,並以“RST 響應”(RSTR) 的格式返回消息。在本文中,我假定頒發的令牌是“安全聲明標記語言”(SAML) 1.1 或 SAML 2.0 令牌。

圖 1 展示了主動令牌頒發時 RST 和 RSTR 的核心內容。RST 消息包括請求安全令牌所需的必要信息,其中涉及待頒發令牌的類型(在本討論中是 SAML)、將要被納入到頒發令牌中的“依賴方”(RP) 所請求的聲明、有關 RP (AppliesTo) 的信息(包括 URL 和通常用於標識 RP 的證書)以及將被用於 RSTR 所返回的“證明密鑰”(proof key) 的可選(未顯示)密鑰材料。

圖 1 主動聯合方案的令牌頒發

如果令牌頒發成功,RSTR 將包括頒發的 SAML 令牌和證明密鑰(假定 STS 決定使用哪個證明密鑰,因此必須在 RSTR 中返回它)。SAML 令牌將包括已驗證方的相關聲明,它由 STS 簽名來保護令牌不被篡改,令牌中將包含用於 RP 加密的證明密鑰,並且它會為 RP 加密自身以確保只有目標接收節點才能處理該令牌。

客戶端使用證明密鑰來簽署發往 RP 的消息。RP 必須能夠在 SAML 令牌中解密證明密鑰,否則拒收該消息。如果令牌中的證明密鑰與消息中的簽名匹配,則證明此次對 RP 的調用是由請求該令牌的調用方所發送的。

被動聯合方案基於 WS-Federation 被動請求者配置文件,其中涉及基於浏覽器的通信。雖然底層消息傳遞仍基於 WS-Trust,但 RST 卻在 STS URL 中被分解為查詢字符串參數,而 RSTR 通常會作為表單參數被發布到 RP。被動 STS 和 RP 使用聯合 Web 處理程序來截取這些參數。被動 STS 可以直接處理 WS-Trust 請求,或者將其傳遞到底層 WS-Trust 實現。圖 2 說明了在被動聯合方案中對 RST 和 RSTR 的處理方式。

圖 2 被動聯合方案的令牌頒發

主動與被動聯合方案的一個本質差異在於頒發的 SAML 令牌的類型。主動聯合通常依靠使用“密鑰所有者”(holder-of-key) 類型主體確認的 SAML 令牌,這意味著正如我前面所述,在實際當中存在著一個證明密鑰,它可以證明發出令牌進行驗證的客戶端是請求該令牌(也稱為 ActAs 行為)的主體。被動聯合方案通常涉及帶有“持有者”(bearer) 類型主體確認的 SAML 令牌(也稱為持有者令牌)。這種類型的令牌不包含證明密鑰,有時也稱為無密鑰令牌。它依靠傳輸來確保從 STS 獲得該令牌並將其傳遞給 RP。

這些概念(WS-Federation、WS-Trust 和 SAML 令牌)是下面要討論的主題的重要背景信息。首先,我將說明如何使用 Geneva 框架來構建主動 STS。然後,我將討論如何構建被動 STS,最後會介紹一些它們各自的擴展方案。

構建自定義主動 STS

在簡單的主動聯合方案中(如圖 3 所示),通常存在以下參與者:

RP,由客戶端調用的服務。

單獨的 STS(也作為服務實現),支持 WS-Trust 協議。此 STS 將對調用方進行身份驗證並頒發具有標識調用方聲明的安全令牌,也稱為標識提供者或 IP-STS。

客戶端,在本例中為基於 Windows 的應用程序,它依靠代理對 STS 進行身份驗證、檢索令牌頒發結果以及發送消息給 RP(它會提供用於身份驗證和授權的頒發令牌)。

圖 3 使用單一 RP 和主動 IP-STS 的簡單聯合方案

圖 4 說明了在此實現中可以靈活設置的部分。其中包括自定義 SecurityTokenService 實現、使用 ServiceHost 擴展 (WSTrustServiceHost) 初始化用於聯合的運行時、使用 WSTrustContract 類型的派生配置一個或多個 WS-Trust 端點以及用於標識模型運行時的其他相關配置設置。在接下來的章節中,我將回顧基於 Geneva 框架的自定義 STS 實現中的各個要素。

圖 4 自定義主動 STS 和主動 IP-STS 的實現體系結構

擴展 SecurityTokenService

Geneva 框架通過 Microsoft.IdentityModel.SecurityTokenService 命名空間中的 SecurityTokenService 類型為構建自定義 STS 提供核心功能。此抽象類擔負著傳遞 RST 和 RSTR 消息和生成安全令牌的重任。自定義 STS 類型將繼承該類並提供(至少)以下功能:

構造函數,用於接受自定義 SecurityTokenServiceConfiguration 實例以配置 STS 的一些基本功能(稍後討論)。

GetScope 的重載函數,用於驗證請求的目標 RP,並為該 RP 提供正確的加密憑據和用於安全令牌的簽名憑據。

GetOutputClaimsIdentity 的重載函數,用於為生成的安全令牌提供聲明。

圖 5 顯示了使用此功能實現的簡單自定義 STS 的部分代碼。請回想一下在圖 1 和圖 2 中介紹過的主動 STS 通信流程。STS 的實現 IdentitySTS 將在調用 GetScope 時驗證傳入 RST——通過驗證 RST 的 AppliesTo 元素是否確實指向信任的 URI。假定 STS 管理著一組可為其頒發令牌的可信 RP 及其證書。如果 AppliesTo 通過了驗證,GetScope 將把該范圍的 EncryptingCredentials 屬性設置為正確的證書,在本例中為 "RPKey"。此外,SigningCredentials 屬性也將被設置為正確的證書,此證書將被用於為頒發的令牌簽名。通常情況下,它是 STS 的私鑰,在本例中為 "IPKey"。

圖 5 簡單的自定義 STS 實現

public class IdentitySTS : SecurityTokenService
{
  public IdentitySTS(SecurityTokenServiceConfiguration config)
    : base( config )
  {
  }
  protected override IClaimsIdentity GetOutputClaimsIdentity(
    IClaimsPrincipal principal, RequestSecurityToken request,
    Scope scope)
  {
    IClaimsIdentity claimsIdentity = new ClaimsIdentity();
    claimsIdentity.Claims.Add(new Claim(ClaimTypes.Name,
      principal.Identity.Name));
    claimsIdentity.Claims.Add(new Claim(ClaimTypes.Role, "Users"));
    return claimsIdentity;
  }
  protected override Scope GetScope(
    Microsoft.IdentityModel.Claims.IClaimsPrincipal principal,
    RequestSecurityToken request)
  {
    Scope scope = new Scope(request);
    scope.EncryptingCredentials = this.GetCredentialsForAppliesTo(
                           request.AppliesTo);
    scope.SigningCredentials = new
     X509SigningCredentials(CertificateUtil.GetCertificate(StoreName.My,
     StoreLocation.LocalMachine, "CN=IPKey"));
    return scope;
  }
  private X509EncryptingCredentials GetCredentialsForAppliesTo(Endpoint
    Address appliesTo)
  {
    if (appliesTo == null || appliesTo.Uri ==null ||
     string.IsNullOrEmpty(appliesTo.Uri.AbsolutePath))
    {
      throw new InvalidRequestException(
        "AppliesTo must be supplied in the RST.");
    }
    X509EncryptingCredentials creds = null;
    if (appliesTo.Uri.AbsoluteUri.StartsWith(
      "http://localhost:8000/RelyingPartyService"))
    {
      creds = new X509EncryptingCredentials(
        CertificateUtil.GetCertificate(StoreName.TrustedPeople,
        StoreLocation.LocalMachine,
        "CN=RPKey"));
    }
    else
      throw new InvalidRequestException(String.Format(
        "Invalid relying party address: {0}",
        appliesTo.Uri.AbsoluteUri));
    return creds;
  }
}

當調用 GetOutputClaimsIdentity 時,運行時將傳遞 ClaimsPrincipal 及經過驗證的調用方標識。此標識通常用於決定要授予調用方的適當聲明。在圖 5 中,代碼將為調用方生成一個名稱聲明和硬編碼的角色聲明,並會以 ClaimsIdentity 的形式返回。此 ClaimsIdentity 將向運行時提供待頒發令牌的聲明。

此 STS 實現還可以針對以下功能進行擴展:

GetOutputClaimsIdentity 可以包括用於在自定義憑據存儲庫中查找用戶及查找其他聲明的代碼。例如,角色列表、有關用戶的其他相關詳細信息(如電子郵件地址),或者代表更精細應用程序權限(如創建、讀取、更新或刪除)的自定義聲明。

GetScope 可以在列出所有可信 RP 及其關聯證書的自定義數據庫中查找 AppliesTo URI。

托管和配置 STS

如果熟悉 WCF,那您一定清楚必須配置一個或多個端點才能使客戶端將消息發送到某個服務。對於 STS 而言,將要用於各個端點的服務約定必須基於 WS-Trust 協議,其中包括四個操作:頒發、驗證、續訂和取消。事實上,STS 能夠實現兩個版本的 WS-Trust 協議:

WS-Trust 1.3:最新版本的 WS-Trust 規范。

WS-Trust 2005 年 2 月:許多行業合作伙伴在等待標准通過認可期間實施的 WS-Trust 版本。

您還可以在 SecurityTokenService 類型中提供 GetScope()、GetOutputClaimsIdentity() 等方法的異步實現。這將提高 I/O 密集型操作(如訪問證書或與聲明數據交互)的可伸縮性。為 STS 配置端點時,必須選擇要為端點公開的約定。Microsoft.IdentityModel.Protocols 命名空間包括兩個用於 STS 端點的服務約定:IWSTrust13SyncContract 和 IWSTrustFeb2005SyncContract。圖 6 顯示了具有兩個端點的 STS 服務配置,每個端點都有一個約定。請注意還有用於實現異步代理的異步版本的約定:IWSTrust13AsyncContract 和 IWSTrustFeb2005AsyncContract。

圖 6 具有多個 WS-Trust 端點的 STS 服務配置

<service name=
 "Microsoft.IdentityModel.Protocols.WSTrust.WSTrustServiceContract"
 behaviorConfiguration="stsBehavior">
 <endpoint address="WSTrustFeb05" binding="wsHttpBinding"
  contract="Microsoft.IdentityModel.Protocols.WSTrust.
  IWSTrustFeb2005SyncContract"/>
 <endpoint address="WSTrust13" binding="wsHttpBinding" 
  contract="Microsoft.IdentityModel.Protocols.WSTrust.
  IWSTrust13SyncContract"/>
</service>

STS 應該基於 WS-Trust 2005 年 2 月的版本公開端點,以便向後兼容早期的客戶端。實現這兩種約定的服務類型是 Microsoft.IdentityModel.Protocols.WSTrust 命名空間中的 WSTrustServiceContract 類型。這是一個應在 STS 的 <service> 配置部分引用的類型。

如圖 4 中的圖表所示,<service> 配置及其端點被用來使用正確的 WSTrustServiceContract 類型初始化主機。在主機初始化期間,還會使用對自定義 SecurityTokenService 實現的引用來初始化此類型。這就是運行時直接將消息傳遞到自定義 STS 的方式。

在圖 6 中,兩個 STS 端點都依賴於 Windows 憑據來驗證調用方(wsHttpBinding 的默認行為)。STS 可以使用其他綁定配置來公開多個端點,以便支持不同的憑據類型。這還包括為每種憑據類型配置正確的安全令牌處理程序。稍後我將討論令牌處理程序的配置設置。

Geneva 框架將提供用於托管 STS 實例的自定義 ServiceHost 類型 WSTrustServiceHost。下面的代碼說明了如何在自托管環境中構造 WSTrustServiceHost 類型:

WSTrustServiceHost stsHost =
 new WSTrustServiceHost(new IdentitySTSConfiguration());
stsHost.Open();

WSTrustServiceHost 依賴自定義 SecurityTokenServiceConfiguration 實例來初始化 WS-Trust 端點的運行時、啟用 STS 的元數據交換行為以及配置元數據交換端點。

在 IIS 中托管時,WSTrustServiceHostFactory 類型用於存檔相同的結果。在 .svc 文件中,@ServiceHost 配置將指定工廠類型和自定義 SecurityTokenServiceConfiguration 類型,如下所示:

<%@ ServiceHost Factory="Microsoft.IdentityModel.Protocols.WSTrust.
 WSTrustServiceHostFactory"
 Service="STS.IdentitySTSConfiguration" %>

工廠在激活後將使用指定的配置來初始化 WSTrustServiceHost。

為 STS 初始化 WSTrustServiceHost 時,必須使用自定義的 SecurityTokenServiceConfiguration 類型。圖 7 顯示了名為 IdentitySTSConfiguration 的自定義實現。

圖 7 自定義 SecurityTokenServiceConfiguration

public class IdentitySTSConfiguration: SecurityTokenServiceConfiguration
{
  public IdentitySTSConfiguration(): base("http://localhost:8010/sts")
  {
   this.TokenIssuerName = "http://localhost:8010/sts";
   this.SigningCredentials = new
    X509SigningCredentials(CertificateUtil.GetCertificate(StoreName.My,
    StoreLocation.LocalMachine, "CN=IPKey"));
   this.SecurityTokenService = typeof( IdentitySTS);
  }
}

此類型必須為 STS 提供一個 URI、簽名的憑據以及對與該配置相關聯的 STS 類型的引用。在 STS 頒發托管卡時,URL 必須有效,否則 Windows CardSpace 將無法導入這些卡。基類型要求為 TokenIssuerName 的構造函數傳入一個字符串值,但我建議在代碼中重載它,以便能夠從配置中動態設置 URI,而不是硬編碼這個需要傳入構造函數的值。

SecurityTokenServiceConfiguration 類型也公開了一些屬性,它們可以用於設置密鑰大小和令牌類型的默認值、禁用對元數據的訪問、控制令牌生存期和時鐘偏差、設置自定義 RST 和 RSTR 序列化程序、配置用來驗證調用方的令牌處理程序以及配置 WS-Trust 端點。

下面的示例顯示了如何禁用元數據訪問以及如何通過編程方式而不是依賴 <service> 配置設置來初始化 WS-Trust 端點(類似於圖 6 中所示):

IdentitySTSConfiguration config = new IdentitySTSConfiguration();
config.DisableWsdl = true;
config.TrustEndpoints.Add(new
 ServiceHostEndpointConfiguration("WSTrustFeb05", new WSHttpBinding(), 
 typeof(IWSTrustFeb2005SyncContract)));
config.TrustEndpoints.Add(new ServiceHostEndpointConfiguration(
 "WSTrust13", new WSHttpBinding(),
 typeof(IWSTrust13SyncContract)));
WSTrustServiceHost stsHost = new WSTrustServiceHost(config);

基本 SecurityTokenServiceConfiguration 類型還通過讀取 <microsoft.identityModel> 配置部分來初始化相關的 STS 配置設置。能夠為自定義 STS 進行聲明性配置的設置包括最大時鐘偏差、用於身份驗證和頒發的安全令牌處理程序、聲明驗證管理器、頒發者名稱注冊以及令牌解析程序。圖 8 顯示了一些為 STS 配置的有用設置。

圖 8 STS 的 <microsoft.identityModel> 配置

<microsoft.identityModel>
 <maximumClockSkew value="00:05:00"/>
 <claimsAuthenticationManager type="STS.
  CustomClaimsAuthenticationManager, STS"/>
 <securityTokenHandlers>
  <remove type="Microsoft.IdentityModel.Tokens.
   WindowsUserNameSecurityTokenHandler,
   Microsoft.IdentityModel, Version=0.5.1.0, Culture=neutral,
   PublicKeyToken=31bf3856ad364e35" />
  <add type="Microsoft.IdentityModel.Tokens.
   MembershipUserNameSecurityTokenHandler,
   Microsoft.IdentityModel, Version=0.5.1.0, Culture=neutral,
   PublicKeyToken=31bf3856ad364e35">
    <usernameSecurityTokenHandlerRequirement
     membershipProvider="CustomProviders.CustomMembershipProvider,
     CustomProviders, Version=1.0.0.0, Culture=neutral,
      PublicKeyToken=c03h5a64f15d0b3f" />
  </add>
 </securityTokenHandlers>
</microsoft.identityModel>

對於非 Windows 憑據,會調用一種自定義的 ClaimsAuthenticationManager 類型,它可以在 GetOutputClaimsIdentity 中頒發聲明之前向運行時提供自定義 ClaimsPrincipal 類型。自定義安全令牌處理程序可能被配置為提供一些設置來覆蓋特定處理程序的默認行為,或更改特定憑據類型的安全令牌處理程序的選項。

安全令牌處理程序

您可能需要一種服務行為配置來確定為每個 STS 端點進行身份驗證和授權時所采用的方式。請回想一下,我在上一篇文章中曾講過 Geneva 框架處理事情的方式略有不同。對於身份驗證來說,<securityTokenHandlers> 集合將指定可用於驗證傳入請求的 SecurityTokenHandler 類型。

此集合只能為每個 SecurityTokenHandler 類型包含一個項目。例如,只能為各憑據類型注冊一個 KerberosSecurityTokenHandler、UserNameSecurityTokenHandler、X509SecurityTokenHandler、Saml11SecurityTokenHandler 或 Saml2SecurityTokenHandler 來處理請求。如果 STS 端點需要 UserName 憑據,則默認注冊的 UserNameSecurityTokenHandler 是 WindowsUserNameSecurityTokenHandler。圖 8 說明了如何刪除 WindowsUserNameSecurityTokenHandler 並添加 MembershipUserNameSecurityTokenHandler 作為其替換處理程序——包括與成員關系提供者相關的配置設置。

您還可以創建自定義 SecurityTokenHandler 類型。請記住,只要它們是從與令牌類別(例如 UserNameSecurityTokenHandler)匹配的相應基類派生而來的,那麼您就可以使用新的自定義處理程序來替換默認處理程序。圖 9 說明了名為 CustomUserNameSecurityTokenHandler 的自定義 UserNameSecurityTokenHandler 實現。

圖 9 自定義 UserNameSecurityTokenHandler

public class CustomUserNameSecurityTokenHandler: 
 UserNameSecurityTokenHandler
{
 public override ClaimsIdentityCollection ValidateToken(SecurityToken token)
 {
  UserNameSecurityToken userNameToken = token as UserNameSecurityToken;
  AuthenticateUser(userNameToken.UserName, userNameToken.Password);
  return new ClaimsIdentityCollection(new IClaimsIdentity[] {
   new ClaimsIdentity(
    new Claim(System.IdentityModel.Claims.ClaimTypes.Name,
     userNameToken.UserName), "CustomUserNameSecurityTokenHandler")});
 }
 public override bool CanValidateToken
 {
  get { return true; }
 }
}

至少,必須在自定義 SecurityTokenHandler 實現中重載 ValidateToken 和 CanValidateToken。在 ValidateToken 內部,您要負責根據相應的憑據存儲庫來驗證身份。該驗證的結果應該是一組聲明,此聲明可被返回給運行時以便附加到請求線程的 ClaimsPrincipal。

圖 10 使用單一 RP 和被動 IP-STS 的簡單聯合方案

重載 CanValidateToken 並返回 True 也非常重要。如果沒有這一重載,令牌處理程序將無法注冊到集合中,從而無法被調用。

構建自定義被動 STS

在簡單的被動聯合方案中,也存在著與主動聯合方案同樣的參與者(如圖 3 所示),不同之處在於其客戶端是浏覽器而 RP 是 Web 應用程序,另外 IP-STS 還需要作為 Web 應用程序的前端以處理基於 HTTP 的通信。圖 10 說明了被動聯合的參與者和通信流。

對於核心 STS 而言,此方案中可以靈活設置的部分與圖 4 中所示的內容相似,但在被動 STS 對身份驗證的處理方式和對底層 STS 功能的調用方式方面也存在著明顯的差異。圖 10 中的圖表從較高級別說明了這些不同之處。被動 STS 是作為網站來實現,它需要 SSL 加密以確保令牌頒發過程的安全。默認頁面 (Default.aspx) 承載了一個通過底層自定義 STS 來促進通信的控件,它的配置過程與主動 STS 一樣。

STS 站點必須在令牌頒發之前驗證調用方,而這正是傳統的 ASP.NET 配置執行其驗證和授權功能的地方。在圖 11 中,STS 應用程序被配置為用於窗體身份驗證,因此如果請求尚未經過 FormsAuthenticationModule 的驗證,則它們將被重定向到登錄頁面 (Login.aspx)。

只需進行一個細微的改動,被動 STS 就可以與主動 STS 共享相同的核心 STS 實現。在 GetScope 重載函數中(如圖 5 所示),被動 STS 必須設置 ReplyToAddress 屬性,只有這樣 STS 才能在頒發令牌後重定向。這通常設置為 RP 的默認頁面(根據隨 RST 提供的 AppliesTo 地址而定):

Scope scope = new Scope(request);
scope.ReplyToAddress = scope.AppliesToAddress + "/default.aspx";
// other scope settings

圖 11 使用窗體身份驗證的被動 STS 的實現體系結構被動 STS 的 Geneva 框架配置與主動 STS 並無二致。SecurityTokenServiceConfiguration 類型被用於初始化 STS(如圖 7 所示),另外還需要考慮 <microsoft.identityModel> 配置部分中的所有相關設置。

FederatedPassiveTokenService 控件

Geneva 框架提供了一個實現被動 STS 必要功能的控件。具體來說,它可以處理登錄和注銷 HTTP 請求,並可以將每個請求轉換到 RST 然後再調用底層 STS 實現。此控件還處理 RSTR 響應和 RP 重定向,並為經過驗證的調用方編寫會話 cookie。

請按照如下方式將此控件放在被動 STS 站點的默認頁面並將其 Service 屬性設置為自定義 SecurityTokenServiceConfiguration 實現:

<idfx:FederatedPassiveTokenService ID="FederatedPassiveTokenService1"
runat="server" Service="STS.IdentityProviderSTSConfiguration, STS">
</idfx:FederatedPassiveTokenService>

此控件要求用戶必須經過身份驗證,它會在其 PreRender 事件中對此進行檢查。需要對 STS 站點進行適當配置以確保在用戶到達此默認頁面之前將其重定向到其他頁面進行身份驗證。
只要經過身份驗證的用戶始終定向到此默認頁面,則無需其他配置即可處理請求。此控件還提供 Error、PreSignInRequested、PostSignInRequested、PreSignOutRequested 和 PostSignOutRequested 事件來處理異常以及掛接登錄和注銷請求。

SessionAuthenticationModule

作為 FederatedPassiveTokenService 控件的替代方法,您可以通過編程方式啟用被動 STS 功能。首先在 <microsoft.identityModel> 配置部分啟用聯合驗證:

<microsoft.identityModel>
<federatedAuthentication enabled="true"/>
</microsoft.identityModel>

然後,為被動 STS 啟用聯合模塊,即來自 Microsoft.IdentityModel.Web 命名空間的 SessionAuthenticationModule 模塊:

<modules>
<add name="SessionAuthentication"
type="Microsoft.IdentityModel.Web.SessionAuthenticationModule,
Microsoft.IdentityModel, Version=0.5.1.0,
Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</modules>

這將得到與 FederatedPassiveTokenService 控件相同的結果,請求將被發送到 STS 網站中的任意頁面。該模塊將把未驗證的調用方重定向到登錄頁面。成功登錄後,調用方將被重定向到最初請求的 STS 頁面。這種編程方法為開發人員提供了除 FederatedPassiveTokenService 控件以外的其他控件。例如,該模塊公開了以下事件以便與初始化、安全令牌管理、登錄和注銷進行交互:ConfigurationLoading、ConfigurationLoaded、SecurityTokenReceived、SecurityTokenValidated、SessionSecurityTokenCreated、SessionSecurityTokenReceived、SignedIn、SigningOut、SignedOut、SignInError 和 SignOutError。

用戶身份驗證

STS 站點負責根據支持的憑據類型來驗證用戶。雖然使用 WCF 實現的主動 STS 能夠輕松配置多個端點來支持不同的身份驗證機制,但被動 STS 只能支持 ASP.NET 網站本身配置的一種身份驗證機制。因此,必須為每種支持的身份驗證機制提供不同的被動 STS 站點。當然,這些站點都可以共享相同的核心 STS 實現。

被動 STS 身份驗證基於 ASP.NET 配置技術。典型選項是 Windows 身份驗證、窗體身份驗證和 Windows CardSpace 身份驗證。對於 Windows 身份驗證,將使用以下配置:

<authentication mode="Windows"/>
<authorization>
<deny users="?"/>
</authorization>

在用戶到達 STS 默認頁面前,會通過交互式對話框對其進行身份驗證,因此在這種情況下不需要自定義登錄頁面。

圖 11 說明了使用窗體身份驗證的示例。FormsAuthenticationModule 將未驗證的調用重定向到登錄頁面,在這裡用戶可以提供憑據。通過驗證後,登錄頁面將重定向到默認 STS 頁面,在這裡聯合控件將繼續處理最初的請求。對應的 ASP.NET 配置如下:

<authentication mode="Forms"/>
<authorization>
<deny users="?"/>
</authorization>

對於 Windows CardSpace 身份驗證,可以將 STS 站點配置為窗體身份驗證,但登錄頁面將包括一個 InformationCard 控件用於 Windows CardSpace 身份驗證。

聲明轉換

聲明轉換對於聯合的安全性至關重要——它可能在聯合過程中的任意時刻發生。在 IP-STS 和 RP 屬於同一域的簡單聯合方案中,IP-STS 負責將聲明從身份驗證期間用戶所提供的一組初始身份標識聲明轉換為 RP 能夠用以授權調用的聲明。用戶可以為 IP-STS 提供任何支持的憑據類型來進行身份驗證,其中每個都會評估一組代表該憑據的聲明。

IP-STS 將這些聲明轉換成一組標准的應用程序聲明,RP 可以通過這組聲明授權調用——它們可以是角色或更精細的聲明,如創建、讀取、更新或刪除等權限。圖 12 中的圖表對這種方案進行了說明,其中 Admin 用戶登錄並被授予 Role 聲明和幾項 Action 聲明,其中包括創建、讀取、更新和刪除等。

圖 12 IP-STS 聲明轉換

這種類型的聲明轉換非常有用,因為對 STS 的身份驗證會得到一個令牌,其中包含授予該用戶的所有聲明。也有一些情況需要使用其他方法進行聲明轉換;例如為了減少頒發給那些僅與當前調用上下文相關的聲明、為了保護聲明的隱私性,或者為了促進跨域聯合等。

向經過驗證的調用方授予與 RP 公開的所有功能相關的完整聲明列表既無必要,有時可能也並不合適。這不但會使列表變得非常長,而且還可能待授予的聲明取決於調用上下文,因此在不具備該上下文的情況下不應授予該聲明。例如,如果某用戶正在與客戶進行訂單交互,則只授予 Delete 聲明即可,但如果他正直接與客戶記錄交互,則不應授予該權限。

在其他一些類似情況中,如果 RP 能夠只從 IP-STS 請求少量聲明即可標識調用方,然後使用一組特定的僅適用於該調用上下文的附加聲明來請求一個新令牌,則將會非常有幫助。例如,如果用戶正在 RP 服務中調用 DeleteCustomer 操作,在授權執行此操作之前,RP 將會調用從 IP-STS 傳入令牌的 RP-STS,並在 DeleteCustomer 操作的上下文中請求 Delete 聲明。如果該聲明存在,則調用將獲得授權。圖 13 中的圖表進一步說明了此示例。

此外,有時還存在 STS 頒發的聲明不應與 RP 直接共享的情況。例如,可以不必通過頒發 Age 聲明來使 RP 了解用戶的年齡,RP 可以請求 IsOver13 聲明來確保調用方的年齡符合使用 RP 功能的條件。這樣可以確保 Age 聲明的實際值不會離開 STS。當然,這也表明 ST 將提供既能避免共享個人詳細信息又可以包含 RP 所需數據的聲明。

圖 13 RP-STS 聲明轉換

當屬於一個域的用戶被授予對另一個域中的 RP 的訪問權限時,聯合方案中也會發生聲明轉換。在這種情況下會涉及兩個 STS——用戶域的 IP-STS 和 RP 所屬域的 RP-STS。

同樣也是在這種情況下,IP-STS 會授予一些 RP-STS 能夠理解且達成一致的聲明;但這些聲明可能無法在 RP 應用程序中直接使用。相反,RP-STS 可能會負責將另一組可信聲明轉換為能夠被 RP 域所理解的聲明。

圖 14 說明了這種方案。當 Joe 試圖在沒有令牌的情況下訪問 RP 時,他將登錄到自己所在域(域 B)的 IP-STS。該請求將申請 RP 能夠理解的聲明(在本例中為 RPClaim),以便 IP-STS 知道應該頒發 RP 能夠使用的令牌。當 RP-STS 接收到此令牌後,它將把該聲明轉換為 RP 專用的聲明。為了使此聯合方案能夠工作,必須在 RP-STS 和 IP-STS 之間建立信任關系,而且它們必須對 IP-STS 應該為其用戶頒發的將被授予對 RP 訪問權限的一組聲明達成一致。

圖 14 聯合方案中的聲明轉換

總結

對於那些熱衷於構建自定義 STS 而又不需要 STS 平台全部功能(如 Geneva 服務器)的用戶而言,Geneva 框架是一種非常受歡迎的實用工具。但即便使用 Geneva 框架,構建自定義 STS 也不是一項簡單的任務,強烈建議您盡量使用完整功能的 STS 以減少出現安全漏洞的風險。

無論使用什麼平台,主動和被動 STS 實現的通信流程都是一致的,並且聲明轉換的原理也相同。與 STS 實現相關的其他概念還包括標識委派和逐級驗證。您可以在 Geneva 框架 SDK 中找到與這些及其他概念相關的示例和文檔。

Michele Leroux Bustamante 是 IDesign Inc. 的首席架構師、聖地亞哥的 Microsoft 區域總監和互聯系統的 Microsoft MVP。她的最新著作是《學習 WCF》。可通過 [email protected] 或訪問 idesign.net 與她取得聯系。Michele 的博客網址是 dasblonde.net。

本文配套源碼

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved