程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 聲明性 WCF安全性

聲明性 WCF安全性

編輯:關於.NET

目錄

安全方案

Intranet 應用程序

Internet 應用程序

企業對企業應用程序

匿名應用程序

無安全性

聲明性安全框架

實現 SecurityBehaviorAttribute

客戶端聲明性安全

SecurityHelper 和 SecureClientBase<T>

這一切意味著什麼

安全性到目前為止是 Windows® Communication Foundation (WCF) 最復 雜的領域。在服務端的每次 WCF 操作調用中,安全都由服務契約、操作契約、 錯誤契約(如果存在)、服務行為、操作行為、宿主配置和方法配置及其代碼等 控制。其中的每一項都可以具有十二個或更多個與安全相關的屬性,如圖 1 所 示,該圖描述了 ServiceHostBase(ServiceHost 的基礎)的多個安全屬性。

圖 1ServiceHostBase 的安全性相關屬性

不僅有多得嚇人的詳細信息要掌握,同時在各個部件之間還存在復雜的關系 ,由此產生了大量可能的排列組合。更加復雜的問題是,並不是所有組合都是允 許或受支持的,所有允許的組合亦不全都是合理或有意義的。

毫不奇怪,編程模型非常復雜就強烈意味著在應用程序級別和業務級別容易 出現問題。允許跨供應商、信任、平台和技術邊界進行安全通信這個目標的底層 復雜性導致了上述結果。任何延伸都不是一項簡單的任務。但是,與 WCF 的其 他方面(如事務、同步和實例化)不同,安全性沒有用於進行配置的屬性或一站 式方法。開發人員僅可以控制現成的原始安全模型。

有很好的理由不將高級安全編程模型包括在內:任何此類模型都不可能解決 所有應用程序和方案,但是遺漏任何應用程序也是不明智的,因為安全在所有方 案中都非常重要。

意識到不可能拿出一個為所有應用程序優化的統一的高級安全模型後,我問 了自己這樣一個問題:有沒有一種模型對大多數應用程序都足夠適用?雖然可解 決所有方案的全面框架不可能實現,但我希望創建一種解決方案,可輕松為絕大 多數情況配置安全設置。在此專欄文章中,我展示了自己的聲明性安全框架。

對於服務,我提供了一種安全屬性;對於客戶端,我提供了一些幫助器類和 安全代理類。我的聲明性框架使安全配置與 WCF 配置的其他方面相同。我想要 一種聲明性模型,該模型可單獨使用並可將了解安全詳情的需要降到最低。作為 一名開發人員,您只需要選擇正確的方案,我的框架會讓配置自動進行。此外, 我的框架要求正確的選項並執行最佳實踐。同時,我希望該模型可提供粒度和對 底層配置的控制(如果需要此類控制)。

在本文中,我將著重講述目標方案、我的框架如何實現以及如何使用該框架 。我也會展示一些說明 WCF 可擴展性的有趣的 WCF 編程技術(您可以從 MSDN® 雜志網站上下載該安全框架)。

安全方案

我的框架支持可用於解決當今大多數應用程序安全需要的五個關鍵方案及某 些細微的變體。這些方案包括:

Intranet 應用程序

Internet 應用程序

企業對企業應用程序

匿名應用程序

無安全性

如果您需要解決不同的方案,您可以遵循我的方法使用隨附的源代碼派生所 需的安全性方面和設置。

Intranet 應用程序

在 Intranet 應用程序中,客戶端和服務都使用 WCF 並且部署在相同的 Internet 上。沒有防火牆隔離客戶端和服務,您可以將基於 Windows 的安全用 於傳輸安全和身份驗證。您可以依靠 Windows 帳戶和組來存儲客戶端的憑據, 並且您可以對 Windows 組使用基於原則、基於角色的安全性驗證成員身份。

對於此方案,我的框架允許使用 Intranet 綁定,即 NetTcpBinding、 NetNamedPipeBinding 和 NetMsmqBinding。使用的綁定是針對最高的保護級別 (經過加密和簽名)配置的。您可以依靠用於傳輸安全的傳輸模式,因為調用總 是點對點的。此外,您可以控制模擬級別,甚至為所有操作請求自動模擬。

Internet 應用程序

在 Internet 方案中,客戶端或服務可能不使用 WCF,甚至不使用 Windows 。另外,Internet 應用程序通常具有大量調用服務的客戶端。這些客戶端調用 來自防火牆外部。您需要依靠 HTTP 來傳輸,並可能有多個中介。

在 Internet 應用程序中,您通常不希望將 Windows 帳戶和組用作憑據。相 反,應用程序需要訪問自定義憑據存儲(盡管如此,您仍可以使用 Windows 安 全,我稍後會對此進行說明)。在此,我的框架為傳輸安全使用消息安全,跨所 有中介提供安全性。客戶端以用戶名和密碼的形式提供憑據。

對於此方案,您應使用 WSHttpBinding 或 WSDualHttpBinding。此外,如果 您具有使用 NetTcpBinding 的 Intranet 應用程序,但不想將 Windows 安全用 於用戶帳戶和組,則您可以在 Intranet 中使用消息安全和客戶用戶名和密碼。

在 Internet 方案中,由於發送到服務的客戶端的消息通過純 HTTP 傳輸, 因此通過使用服務提供的證書對消息的內容(客戶端的憑據和消息的正文)進行 加密以保護消息內容至關重要。WCF 支持選用不同方式來獲取該證書。例如,客 戶端開發人員可以使用任何帶外機制(例如電子郵件或公共網頁)獲取服務證書 。

使用 WCF 配置架構,客戶端可以將關於服務證書的詳細信息(如證書在客戶 端的存儲位置以及如何找到證書)包括在其配置文件(尤其是端點行為部分)中 。到目前為止,從客戶端的角度來看,這是最安全的選擇了,因為任何推翻客戶 端的地址解析並將調用重定向到惡意服務的企圖都會由於其他服務沒有正確的證 書而失敗。不過,由於每次客戶端都需要與不同的服務交互,客戶端管理員需要 修正客戶端的配置文件,這也是靈活性最小的選擇。

顯式引用客戶端可能與之交互的所有服務的證書的一個可行替代方案是,將 那些證書存儲在客戶端的 Trusted People 證書文件夾中,並指示 WCF 僅允許 調用其證書位於該文件夾下的服務。在該情況下,客戶端需要在運行時作為初始 預調用協商的一部分獲取服務證書,檢查該證書是否位於 Trusted People 存儲 中,如果位於此位置,則繼續使用它以保護消息。這種證書協商形式是 WS 綁定 的默認行為。對於 Internet 方案,我使用證書協商,還驗證客戶端的 Trusted People 存儲中的服務證書。這稱為對等信任驗證。

一旦客戶端的用戶名和密碼憑據被服務接收,宿主就會對它們進行身份驗證 。默認情況下,憑據作為 Windows 憑據(與主持域和機器相對)進行身份驗證 ,但是您可以將服務配置為使用 ASP.NET 成員身份提供程序。

服務可以使用基於原則、基於角色的安全:如果憑據是 Windows 憑據,那麼 指定的角色必須是 Windows 組。如果使用 ASP.NET 提供程序,則使用 ASP.NET 角色提供程序驗證角色。

企業對企業應用程序

在企業對企業方案中,服務及其客戶端是完全不同的業務實體。他們不共享 憑據或帳戶,憑據或帳戶之間的通信通常對外界關閉。與服務交互的客戶端相對 較少,在詳細的業務協議和其他條件都滿足後,客戶端方可與服務交互。

客戶端不使用 Windows 帳戶或用戶名,而是使用 X509 證書向服務標識自身 ,X509 證書通常是服務已知的。客戶端或服務可能不使用 WCF,甚至不使用 Windows。客戶端調用來自防火牆外部,您需要依靠 HTTP 進行傳輸,並可能有 多個中介。

對於企業對企業方案,我的框架允許使用 BasicHttpBinding、 WSHttpBinding 或者 WSDualHttpBinding。您必須為傳輸安全使用消息安全以跨 所有中介提供安全性。在此方案中,使用服務端證書保護消息,就像在 Internet 方案中一樣。但是,與 Internet 方案不同,此方案中的客戶端提供 證書形式的憑據。客戶端將配置文件中的(或編程方式的)證書提供給代理,代 理將證書捆綁在消息中,並將其發送到服務。

WCF 為服務管理員提供了多種方式來驗證客戶端發送的證書。如果證書通過 驗證,則認為客戶端經過了身份驗證。我的框架使用服務端上的同等信任驗證, 因此,服務管理員應安裝被允許與在服務的本地機器上的 Trusted People 存儲 中的服務進行交互的客戶端的所有證書。

當客戶端的證書被服務接收後,如果在受信任的存儲中可以找到該證書,則 表明客戶端已通過身份驗證。默認情況下,服務無法使用基於原則、基於角色的 安全。這是因為提供的憑據(即客戶端的證書)未映射到 Windows 或者 ASP.NET 用戶帳戶。由於企業對企業端點往往專用於客戶端的較小集合乃至特定 的客戶端,因此缺乏身份驗證支持可能不會導致問題。另一方面,如果您仍希望 授權客戶端,那麼即使成員身份提供程序未用於身份驗證,我的框架也可以利用 ASP.NET 角色提供程序進行授權。在 WCF 中,能夠單獨使用提供程序是 ASP.NET 提供程序模型的核心設計目標。

匿名應用程序

在匿名方案中,客戶端無需提供任何憑據即可訪問服務 — 這些客戶端 是無法識別的。另一方面,客戶端和服務不需要防止篡改和嗅探的安全消息傳輸 。面向 Internet 和基於 Intranet 的應用程序可能都需要提供匿名且安全的訪 問。

匿名方案可以擁有任意數目的客戶端。客戶端可能通過 HTTP 或 TCP 進行連 接。保護消息的需要以及客戶端可能使用多個中介通過 Internet 進行調用的事 實,表示您應該使用消息安全。對於此方案,您可以使用 NetTcpBinding、 WSHttpBinding、WSDualHttpBinding 和 NetMsmqBinding — Internet 和 intranet 綁定的混合。注意,您不能使用 BasicHttpBinding、 NetNamedPipeBinding、NetPeerTcpBinding 或 WSFederationHttpBinding,因 為這些綁定不支持消息安全或者不支持在消息中沒有憑據。

請再次注意,匿名方案中不進行客戶端身份驗證,客戶端不需要向代理提供 憑據。對於客戶端的身份驗證以及消息保護,服務需要提供其證書。由於客戶端 是匿名的(且未經過身份驗證),因此將排除授權和基於角色的安全性。

無安全性

在這最後一個方案中,您的應用程序完全關閉了安全性。服務不依靠任何傳 輸安全,並且不對調用方進行身份驗證或授權。服務可以接受任意數目的客戶端 ,並且客戶端不需要向代理提供任何憑據。由於客戶端是匿名的(且未經過身份 驗證),因此將排除授權和基於角色的安全性。Internet 和 Intranet 服務都 可以配置為無安全性,但是,不用說,此類服務是完全公開的。您一般需要業務 證明來重新釋放安全性。

圖 2 和圖 3 可用作安全方案的關鍵元素摘要。圖 2 列出了每個方案中使用 的綁定,圖 3 顯示了每個安全性方面如何與每個方案相關。

Figure3安全方案方面

方面 Intranet Internet 企業到企業 匿名 無安全性 傳輸 是 否 否 否 否 消息 否 是 是 是 否 服務身份驗證 Windows 證書 證書 證書 否 客戶端身份驗證 Windows ASP.NET 證書 否 否 授權 Windows ASP.NET No/ASP.NET 否 否 模擬 是 否 否 否 否

Figure2安全方案和綁定

綁定 Intranet Internet 企業到企業 匿名 無安全性 BasicHttpBinding 否 否 是 否 是 NetTcpBinding 是 是 否 是 是 NetPeerTcpBinding 否 否 否 否 是 NetNamedPipeBinding 是 否 否 否 是 WSHttpBinding 否 是 是 是 是 WSDualHttpBinding 否 是 是 是 是 NetMsmqBinding 是 否 否 是 是

聲明性安全框架

圖 4 列出了 SecurityBehaviorAttribute 和 ServiceSecurity 枚舉的定義 。ServiceSecurity 定義了我的框架支持的五個方案。

Figure4SecurityBehaviorAttribute

public enum ServiceSecurity
{
  None,
  Anonymous,
  BusinessToBusiness,
  Internet,
  Intranet
}
[AttributeUsage(AttributeTargets.Class)]
public class SecurityBehaviorAttribute : Attribute,IServiceBehavior
{
  public SecurityBehaviorAttribute(ServiceSecurity mode);
  public SecurityBehaviorAttribute(ServiceSecurity mode,
   string serviceCertificateName);
  public SecurityBehaviorAttribute(ServiceSecurity mode,
   StoreLocation storeLocation,
   StoreName storeName,
   X509FindType findType,
   string serviceCertificateName);
  public bool ImpersonateAll {get;set;}
  public string ApplicationName {get;set;}
  public bool UseAspNetProviders {get;set;}
}

應用 SecurityBehaviorAttribute 時,您需要以 ServiceSecurity 值的形 式提供目標方案。您僅可以使用 SecurityBehaviorAttribute 的構造函數,或 者您可以設置屬性。如果未進行設置,則屬性默認為目標方案上下文中的合理值 。選擇方案時,配置的行為遵循我以前對各個方案的描述。

SecurityBehaviorAttribute 產生了允許少數排列和子方案的可組合安全性 模型。使用該屬性時,您甚至可以擁有無安全性的宿主配置文件,或者您可以將 配置文件中的設置與由該屬性確定的值結合起來。使用同樣的方法,您的宿主代 碼也可以沒有安全性,或者您可以將編程宿主安全性與該屬性結合起來。

要為 Intranet 安全性方案配置服務,可應用帶有 ServiceSecurity.Intranet 的 SecurityBehavior,例如:

[ServiceContract]
interface IMyContract
{
  [OperationContract]
  void MyMethod();
}
[SecurityBehavior(ServiceSecurity.Intranet)]
class MyService : IMyContract
{
  public void MyMethod() {...}
}

即便使用的服務契約可能不包含保護級別,屬性也會以編程方式添加命令以 強制執行消息保護。您可以將 Windows NT 組用於基於角色的安全,例如:

[SecurityBehavior(ServiceSecurity.Intranet)]
class MyService : IMyContract
{
  [PrincipalPermission(SecurityAction.Demand,
    Role = @”<Domain>\Customer”)]
  public void MyMethod() {...}
}

服務可以按編程方式模擬調用方或將操作行為屬性用於單個方法模擬。您還 可以通過 ImpersonateAll 屬性將服務配置為自動模擬所有方法中的所有調用方 。ImpersonateAll 默認為“false”,但是當設置為 “true”時,該屬性無需應用任何操作行為屬性即可模擬所有操作中 的所有調用方:

[SecurityBehavior(ServiceSecurity.Intranet,ImpersonateAll = true)]
class MyService : IMyContract {...}

使用 Internet 方案,您需要使用 ServiceSecurity.Internet 進行配置並 選擇要使用的服務證書。注意,在圖 4 中,ServiceBehavior 屬性構造函數可 采用服務證書名稱。如果未指定,服務證書與原始 WCF 安全一樣從宿主配置文 件中加載:

[SecurityBehavior(ServiceSecurity.Internet)]
class MyService : IMyContract {...}

您也可以指定服務證書名稱,在這種情況下,指定的證書根據名稱從“ 我的文件夾”中的 LocalMachine 存儲進行加載:

[SecurityBehavior (ServiceSecurity.Internet,”MyServiceCert”)]
class MyService : IMyContract {...}

如果證書名稱設置為空字符串,則該屬性將使用證書名稱的主機名稱(或域 )推斷證書名稱,並根據名稱從“我的文件夾”中的 LocalMachine 存儲加載證書:

[SecurityBehavior(ServiceSecurity.Internet,””)]
class MyService : IMyContract {...}

最終,該屬性允許您顯式指定存儲位置、存儲名稱和查找方法:

[SecurityBehavior(ServiceSecurity.Internet,
  StoreLocation.LocalMachine,StoreName.My,
  X509FindType.FindBySubjectName,”MyServiceCert”)]
class MyService : IMyContract {...}

注意,您可以將顯式位置與推斷的證書名稱相結合,例如:

[SecurityBehavior(ServiceSecurity.Internet,
  StoreLocation.LocalMachine,StoreName.My,
  X509FindType.FindBySubjectName,””)]
class MyService : IMyContract {...}

根據哪種憑據存儲對客戶端進行身份驗證是由 UseAspNetProviders 屬性指 定的。此屬性默認為“false”,表示默認設置是將客戶端的用戶名 和密碼作為 Windows 憑據進行身份驗證:

[SecurityBehavior (ServiceSecurity.Internet,”MyServiceCert”)]
class MyService : IMyContract
{
  [PrincipalPermission(
   SecurityAction.Demand,Role = @”<Domain>\Customer”)]
  public void MyMethod() {...}
}

您甚至可以模擬所有調用方:

[SecurityBehavior(
  ServiceSecurity.Internet,”MyServiceCert”,ImpersonateAll = true)]
class MyService : IMyContract {...}

如果 UseAspNetProviders 設置為“true”,則該屬性將使用 ASP.NET 成員身份和角色提供程序,就像 Internet 方案中描述的那樣:

[SecurityBehavior(
  ServiceSecurity.Internet,”MyServiceCert”,UseAspNetProvider s = true)]
class MyService : IMyContract
{
  [PrincipalPermission(SecurityAction.Demand,Role = “Manager”)]
  public void MyMethod() {...}
}

該屬性將以編程方式啟用配置文件中的角色管理器節,以便宿主配置文件中 不需要除端點以外的任何內容。

下一個問題是使用 ApplicationName 屬性為 ASP.NET 提供程序提供應用程 序名稱。如果未分配值,該屬性將與原始 WCF 一樣從配置文件中查找應用程序 名稱。如果沒有在宿主配置文件中查找到任何值,該屬性不會默認使用無意義的 /來自 machine.config 的名稱。相反,它會默認為宿主程序集名稱。如果為 ApplicationName 屬性分配了值,則它會覆蓋宿主配置文件中提供的應用程序名 稱:

[SecurityBehavior (ServiceSecurity.Internet,”MyServiceCert”,
 UseAspNetProviders = true,ApplicationName = “MyApplication”)]
class MyService : IMyContract {...}

為企業對企業方案進行配置要求將 ServiceSecurity 設置為 ServiceSecurity.BusinessToBusiness,如同 ServiceSecurity.Internet 一樣 。例如:

[SecurityBehavior(ServiceSecurity.BusinessToBusiness)]
class MyService : IMyContract {...}
[SecurityBehavior(ServiceSecurity.BusinessToBusiness,””)]
class MyService : IMyContract {...}
[SecurityBehavior (ServiceSecurity.BusinessToBusiness,”MyServiceCert”)]
class MyService : IMyContract {...}

默認情況下,當使用 ServiceSecurity.BusinessToBusiness 時,該服務無 法授權其調用方。但是,將 UseAspNetProviders 屬性設置為 true,您就能夠 使用 ASP.NET 角色提供程序:

[SecurityBehavior(ServiceSecurity.BusinessToBusiness,
  UseAspNetProviders = true)]
class MyService : IMyContract {...}

當使用 ASP.NET 角色提供程序時,系統將按照查找和確定 ServiceSecurity.Internet 設置的方式查找和確定該應用程序的名稱:

[SecurityBehavior (ServiceSecurity.BusinessToBusiness,”MyServiceCert”,
  UseAspNetProviders = true,ApplicationName = “MyApplication”)]
class MyService : IMyContract {...}

要根據匿名方案允許調用方,您需要配置 ServiceSecurity.Anonymous 的屬 性。為其配置服務證書的過程與 ServiceSecurity.Internet 相同:

[SecurityBehavior(ServiceSecurity.Anonymous)]
class MyService : IMyContract {...}

要像在無安全性方案中一樣徹底關閉安全性,僅提供 ServiceSecurity.None 的屬性即可:

[SecurityBehavior(ServiceSecurity.None)]
class MyService : IMyContract {...}

實現 SecurityBehaviorAttribute

圖 5 是實現 SecurityBehaviorAttribute 的部分列表。該屬性的這三個公 共屬性分別對應三個私有成員,該屬性將配置後的值保存在這三個私有成員中。

Figure5實現 SecurityBehaviorAttribute

[AttributeUsage(AttributeTargets.Class)]
class SecurityBehaviorAttribute : Attribute,IServiceBehavior
{
  SecurityBehavior m_SecurityBehavior;
  string m_ApplicationName = String.Empty;
  bool m_UseAspNetProviders;
  bool m_ImpersonateAll;
  public SecurityBehaviorAttribute(ServiceSecurity mode)
  {
   m_SecurityBehavior = new SecurityBehavior(mode);
  }
  public SecurityBehaviorAttribute(ServiceSecurity mode,
   string serviceCertificateName)
  {
   m_SecurityBehavior =
     new SecurityBehavior(mode,serviceCertificateName);
  }
  public bool ImpersonateAll {get;set;} // m_ImpersonateAll
  public string ApplicationName {get;set;} // m_ApplicationName
  public bool UseAspNetProviders {get;set;} // m_UseAspNetProviders
  void IServiceBehavior.AddBindingParameters(
   ServiceDescription description,
   ServiceHostBase serviceHostBase,
   Collection<ServiceEndpoint> endpoints,
   BindingParameterCollection parameters)
  {
   m_SecurityBehavior.AddBindingParameters(
     description,serviceHostBase, endpoints,parameters);
  }
  void IServiceBehavior.Validate(ServiceDescription description,
   ServiceHostBase serviceHostBase)
  {
   m_SecurityBehavior.UseAspNetProviders = UseAspNetProviders;
   m_SecurityBehavior.ApplicationName = ApplicationName;
   m_SecurityBehavior.ImpersonateAll = ImpersonateAll;
   m_SecurityBehavior.Validate(description,serviceHostBase);
  }
  ... //Rest of the implementation
}

SecurityBehaviorAttribute 是服務行為屬性,因此您可以對服務類直接應 用該屬性。該屬性支持 WCF 擴展接口 IServiceBehavior,其定義如下:

public interface IServiceBehavior
{
  void AddBindingParameters(ServiceDescription serviceDescription,
   ServiceHostBase serviceHostBase,
   Collection<ServiceEndpoint> endpoints,
   BindingParameterCollection bindingParameters);
  void Validate(ServiceDescription serviceDescription,
   ServiceHostBase serviceHostBase);
  void ApplyDispatchBehavior(...);
}

IServiceBehavior 是一種特殊的接口。如果服務類上的某個屬性支持此接口 ,當啟動宿主,然後實例化服務後,WCF 將調用 IServiceBehavior 的各種方法 ,從而使服務可以掛接和擴展 WCF 並影響到綁定、調度程序和宿主的配置等。

調用 IServiceBehavior 的 AddBindingParameters 方法後, SecurityBehaviorAttribute 強制實施與所需方案相匹配的綁定配置。 SecurityBehaviorAttribute 在 IServiceBehavior 的 Validate 方法中配置宿 主。實際配置是使用名為“SecurityBehavior”的幫助器類完成的。 一般而言,我建議將擴展的實際代碼與屬性分離,以便可在別處使用該代碼。例 如,本文隨附的代碼也包含一個宿主類,該宿主類可在托管服務類上應用聲明性 安全,仿佛該類使用了 SecurityBehaviorAttribute 一樣。我的框架的客戶端 部分也使用 SecurityBehavior,方式類似。SecurityBehaviorAttribute 構造 SecurityBehavior 實例,同時在相應的構造函數中向其提供方案(模式參數) 以及證書名稱。SecurityBehavior 提供了所有安全方案的細致的系統化編程設 置。這樣就使得配置文件和宿主代碼脫離了安全風險。

SecurityBehavior 本身是一種服務行為,設計為獨立於屬性而使用。圖 6 包含 SecurityBehavior 的部分列表。

Figure6實現 SecurityBehavior

class SecurityBehavior : IServiceBehavior
{
  ServiceSecurity m_Mode;
  StoreLocation m_StoreLocation;
  StoreName m_StoreName;
  X509FindType m_FindType;
  string m_SubjectName;
  bool m_UseAspNetProviders;
  string m_ApplicationName = String.Empty;
  bool m_ImpersonateAll;
  public SecurityBehavior(ServiceSecurity mode) :
   this(mode,StoreLocation.LocalMachine,StoreName.My,
     X509FindType.FindBySubjectName,null) {}
  public SecurityBehavior(ServiceSecurity mode,
   StoreLocation storeLocation,StoreName storeName,
   X509FindType findType,string subjectName) 
    {...} //Sets the corresponding members
  public bool ImpersonateAll {get;set;} // m_ImpersonateAll
  public bool UseAspNetProviders {get;set;} //m_UseAspNetProviders
  public string ApplicationName {get;set;} // m_ApplicationName
  public void Validate(ServiceDescription description,
   ServiceHostBase serviceHostBase)
  {
   if(m_SubjectName != null)
   {
     switch(m_Mode)
     {
      case ServiceSecurity.Anonymous:
      case ServiceSecurity.BusinessToBusiness:
      case ServiceSecurity.Internet:
         string subjectName = m_SubjectName != String.Empty ?
          m_SubjectName :
          description.Endpoints[0].Address.Uri.Host;
        serviceHostBase.Credentials.ServiceCertificate.
         SetCertificate(m_StoreLocation,m_StoreName,
          m_FindType,subjectName);
        break;
     }
   }
   ...
  }
  public void AddBindingParameters(ServiceDescription description,
   ServiceHostBase serviceHostBase,
   Collection<ServiceEndpoint> endpoints,
   BindingParameterCollection parameters)
  {
   ...
   switch(m_Mode)
   {
     case ServiceSecurity.Intranet:
      ConfigureIntranet(endpoints);
      break;
     case ServiceSecurity.Internet:
      ConfigureInternet(endpoints,UseAspNetProviders);
      break;
     ...
   }
  }
  internal static void ConfigureInternet(
   Collection<ServiceEndpoint> endpoints)
  {
   foreach(ServiceEndpoint endpoint in endpoints)
   {
     Binding binding = endpoint.Binding;
     if(binding is WSHttpBinding)
     {
      WSHttpBinding wsBinding = (WSHttpBinding)binding;
      wsBinding.Security.Mode = SecurityMode.Message;
      wsBinding.Security.Message.ClientCredentialType =
        MessageCredentialType.UserName;
      continue;
     }
     ...
     throw new InvalidOperationException(binding.GetType() +
      “is unsupprted with ServiceSecurity.Internet”);
   }
  }
  ... //Rest of the implementation
}

SecurityBehavior 構造函數將其參數(如安全模式和證書詳細信息)存儲在 成員變量中。Validate 方法是決策樹,它根據方案和提供的信息配置宿主,支 持本文前面描述的 SecurityBehaviorAttribute 行為。

AddBindingParameters 為每個方案調用一種專用幫助器方法來配置宿主的端 點集合。每種幫助器方法(如 ConfigureInternet)將循環訪問該集合,並且對 於每個端點,還將驗證正在使用的綁定與方案相匹配,然後根據該方案對綁定進 行配置。

客戶端聲明性安全

WCF 不支持在代理類上應用屬性。即使支持,客戶端也可能需要在運行時提 供憑據和其他設置。作為補償,在客戶端上支持聲明性安全的第一步是我的 SecurityHelper 靜態幫助器類,如圖 7 中所示。

Figure7SecurityHelper 幫助器類

public static class SecurityHelper
{
  public static void UnsecuredProxy<T>(ClientBase<T> proxy)
   where T : class;
  public static void AnonymousProxy<T>(ClientBase<T> proxy)
   where T : class;
  public static void SecureProxy<T>(ClientBase<T> proxy,
   string userName,string password) where T : class;
  public static void SecureProxy<T>(ClientBase<T> proxy,
   string domain,string userName,string password) where T : class;
  public static void SecureProxy<T>(ClientBase<T> proxy,
   string clientCertificateName) where T : class;
  public static void SecureProxy<T>(ClientBase<T> proxy,
   StoreLocation storeLocation,StoreName storeName,
   X509FindType findType,string clientCertificateName)
   where T : class;
  ... //More members
}

SecurityHelper 使您可以根據所需安全方案和行為配置普通代理。您僅可以 在打開代理前對其進行配置。無需在客戶端的配置文件中或客戶端代碼中的其他 位置進行任何安全設置。SecurityHelper 具有智能,它會根據提供的參數和調 用方法選擇正確的安全行為。例如,下面介紹了如何保護 Intranet 方案的代理 並為其提供客戶端的 Windows 憑據:

MyContractClient proxy = new MyContractClient();
SecurityHelper.SecureProxy (proxy,”MyDomain”,”MyUsername”,”MyPasswo rd”);
proxy.MyMethod();
proxy.Close();

對於 Internet 方案,客戶端僅需提供用戶名和密碼(請記住,由服務端確 定這些是 Windows 還是 ASP.NET 提供程序憑據):

MyContractClient proxy = new MyContractClient();
SecurityHelper.SecureProxy (proxy,”MyUsername”,”MyPassword”);
proxy.MyMethod();
proxy.Close();

對於企業到企業方案,如果客戶端希望像在原始 WCF 中一樣在其配置文件中 使用證書,那麼它可以為客戶端證書名稱指定空字符串,否則,可以顯式列出證 書名稱:

MyContractClient proxy = new MyContractClient();
SecurityHelper.SecureProxy(proxy,”MyClientCert”);
proxy.MyMethod();
proxy.Close();

SecurityHelper 將根據名稱從“我的文件夾”中的客戶端 LocalMachine 存儲中加載證書。客戶端也可以指定查找和加載證書所需的所有 信息。對於匿名客戶端,請使用 AnonymousProxy 方法:

MyContractClient proxy = new MyContractClient();
SecurityHelper.AnonymousProxy(proxy);
proxy.MyMethod();
proxy.Close();

對於無安全性,請使用 UnsecuredProxy 方法:

MyContractClient proxy = new MyContractClient();
SecurityHelper.UnsecuredProxy(proxy);
proxy.MyMethod();
proxy.Close();

SecurityHelper 和 SecureClientBase<T>

在內部,SecurityHelper 不僅使用 SecurityBehavior 設置憑據,而且還使 用 SecurityBehavior 配置代理的端點(請參見圖 8)。

Figure8實現 SecurityHelper

public static class SecurityHelper
{
  public static void SecureProxy<T>(ClientBase<T> proxy,
   string userName,string password) where T : class
  {
   if(proxy.State == CommunicationState.Opened)
   {
     throw new InvalidOperationException(
      “Proxy channel is already opened”);
   }
   Collection<ServiceEndpoint> endpoints =
     new Collection<ServiceEndpoint>();
   endpoints.Add(proxy.Endpoint);
   SecurityBehavior.ConfigureInternet(endpoints);
   proxy.ClientCredentials.UserName.UserName = userName;
   proxy.ClientCredentials.UserName.Password = password;
   proxy.ClientCredentials.ServiceCertificate.Authentication.
   CertificateValidationMode = X509CertificateValidationMode.PeerTrust;
  }
  ... //Rest of the implementation
}

使用 SecurityHelper 的好處在於它可以在任何代理上運行 — 甚至是 客戶端開發人員不負責其創建的代理。如果您負責生成代理,您可以使用 SecureClientBase<T> 類,如圖 9 所示。

Figure9SecureClientBase<T> 類

public abstract class SecureClientBase<T> : ClientBase<T> where T : class
{
  //These constructors target the default endpoint
  protected SecureClientBase();
  protected SecureClientBase(ServiceSecurity mode);
  protected SecureClientBase(string userName,string password);
  protected SecureClientBase(
   string domain,string userName,string password);
  protected SecureClientBase(string clientCertificateName);
  protected SecureClientBase(StoreLocation storeLocation,
   StoreName storeName,X509FindType findType,
   string clientCertificateName);
  ... //More constructors for other types of endpoints
}

SecureClientBase<T> 由傳統的 ClientBase<T> 派生而來,並 且增添了聲明性安全支持。您需要從 SecureClientBase<T> 而非 ClientBase<T> 派生您的代理,提供與您的安全方案相匹配的構造函數, 並使用提供的憑據和端點信息調用 SecureClientBase<T> 的基本構造函 數,如下所示:

class MyContractClient : SecureClientBase<IMyContract>,IMyContract
{
  public MyContractClient(ServiceSecurity mode) : base(mode) {}
  public MyContractClient(string userName,string password) :
   base(userName,password) {}
  ... // More constructors
  public void MyMethod()
  {
   Channel.MyMethod();
  }
}

使用派生代理非常簡單。例如,對於 Internet 方案,應如下所示:

MyContractClient proxy = new MyContractClient (“MyUsername”,
                        ”MyPassword”);
proxy.MyMethod();
proxy.Close();

對於匿名方案,應如下所示:

MyContractClient proxy = new MyContractClient (ServiceSecurity.Anonymous);
proxy.MyMethod();
proxy.Close();

如圖 10 所示,要實現 SecureClientBase<T>,使用 SecurityHelper 即可。

Figure10實現 SecureClientBase<T>

public class SecureClientBase<T> : ClientBase<T> where T : class
{
  protected SecureClientBase(ServiceSecurity mode)
  {
   switch(mode)
   {
     case ServiceSecurity.None:
      SecurityHelper.UnsecuredProxy(this);
      break;
     case ServiceSecurity.Anonymous:
      SecurityHelper.AnonymousProxy(this);
      break;
     ...
   }
  }
  protected SecureClientBase(string userName,string password)
  {
   SecurityHelper.SecureProxy(this,userName,password);
  }
  ... //More constructors
}

當不使用代理類時,您可以像使用 WCF 提供的通道工廠一樣使用我的 SecureChannelFactory(包括在本文的源代碼中)。但使用我的 SecureChannelFactory,您可以利用聲明性安全。

這一切意味著什麼

由於支持客戶端和服務在可能范圍內進行最大程度的系列交互,WCF 安全帶 來了難以掌控的復雜性。我的聲明性安全框架的目標是在不降低支持方案的安全 性或配置靈活性的情況下消除復雜性。您需要做的僅是為您的應用程序選擇正確 的安全方案,並且您仍可以擁有如同對原始 WCF 安全控制一般的控制。如果您 希望了解有關原始 WCF 安全工作原理的詳細信息,請參閱 2006 年 8 月安全簡 報專欄。

與我這個框架類似的框架的不利一面是它並不涵蓋所有的方案(例如,聯合 安全)。但您可以將我的框架用作支持聲明性安全的起點,然後再將其自定義為 您的特定方案。

請將您的問題和意見發送至[email protected].

Juval Lowy是 IDesign 的一名提供 WCF 培訓和 WCF 體系結構咨詢的軟件架 構師。本文包含他最近編寫的《Programming WCF Services》(O’Reilly ,2007)一書的摘要。另外,他還是 Microsoft 硅谷地區的區域總監。您可以 通過 idesign.net 與 Juval 聯系。

本文配套源碼

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