本文章是由機器翻譯。

Geneva 框架

構建自訂安全權杖服務

Michele Leroux Bustamante

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

本文將介紹以下內容:

  • 使用 Geneva 框架實現安全權杖服務
  • 聯合安全性
  • 聲明轉換
本文使用了以下技術:
Windows Communication Foundation、ASP.NET、Geneva 框架

代碼下載位置:MSDN 代碼庫
線上流覽代碼

目錄

安全權杖服務入門
構建自訂主動 STS
擴展 SecurityTokenService
託管和配置 STS
安全權杖處理常式
構建自訂被動 STS
FederatedPassiveTokenService 控制項
SessionAuthenticationModule
使用者身份驗證
聲明轉換
總結

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) 的可選(未顯示)金鑰材料。

fig01.gif

圖 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 的處理方式。

fig02.gif

圖 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(它會提供用於身份驗證和授權的頒發權杖)。

fig04.gif

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

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

fig04.gif

圖 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。

fig10.gif

圖 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

fig11.gif

圖 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 聲明,其中包括創建、讀取、更新和刪除等。

fig12.gif

圖 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 所需資料的聲明。

fig13.gif

圖 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 存取權限的一組聲明達成一致。

fig14.gif

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

總結

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

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

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