閱讀138 返回首頁    go 阿裏雲 go 技術社區[雲棲]


[WCF權限控製]從兩個重要的概念談起:Identity與Principal[上篇]

在安全領域,認證和授權是兩個重要的主題。認證是安全體係的第一道屏障,守護著整個應用或者服務的第一道大門。當訪問者叩門請求進入的時候,認證體係通過驗證對方提供憑證確定其真實身份。作為看門人的認證體係,隻有在證實了訪問者的真實身份的情況下才會為其打開城門,否則將之舉之門外。

當訪問者入門之後,並不意味著它可以為所欲為。為了讓適合的人幹適合的事,就需要授權機製為具體的人設置具體的權限,並根據這些權限設置決定試圖調用的操作或者訪問的資源對該訪問者是否是安全的。對於一個安全保障體係來說,授權是目的。但是授權的執行是假定已經通過認證體現確定了訪問者真實身份,因為用於進行授權采用的權限集是基於真個確定的身份的。在真正進入對WCF授權的具體介紹之前,我們有必要來了解一下這個“身份”的問題。

目錄
一、IIdentity
二、WinodwsIdentity
三、GenericIdentity
四、X509Identity
五、服務安全上下文中的身份

在.NET的安全應用編程接口中,身份通過System.Security.Principal.IIdentity接口表示。從下麵表示IIdentity接口定義的代碼片斷中,我們可以看到這個接口定義其實很簡單,它具有如下三個隻讀屬性:

  • Name:身份所代表的用戶的名稱;
  • IsAuthenticated:身份所代表的用戶是否經過認證;
  • AuthenticationType:身份認證所采用的類型。
   1: public interface IIdentity
   2: {
   3:     string AuthenticationType { get; }
   4:     bool   IsAuthenticated { get; }
   5:     string Name { get; }
   6: }

通過IIdentity表示的身份是基於某種認證類型的,不同類型的認證往往對應於不同的身份類型。以ASP.NET認證為例,如果我們采用Forms認證,那麼認證後的身份通過一個FormsIdentity對象表示。而Windows Live Passport認證對應的具體身份類型則是PassportIdentity。在這裏我們著重介紹一下如下三種身份類型:WindowsIdentity、GenericIdentity和X509Identity。

WindowsIdentity定義在System.Security.Principal命名空間下。顧名思義,WindowsIdentity用於表示一個基於Windows認證的身份。一個采用WindowsIdentity定義的Windos身份具有一係列的屬性,它們主要定義在如下的代碼片斷中。

   1: public class WindowsIdentity : IIdentity, ...
   2: {    
   3:     //其他成員
   4:     public virtual string Name { get; }    
   5:     public string AuthenticationType {  get; }
   6:     
   7:     public virtual bool IsAuthenticated { get; }
   8:  
   9:     public IdentityReferenceCollection Groups { get; }
  10:     public virtual bool IsAnonymous {  get; }
  11:     public virtual bool IsGuest { get; }
  12:     public virtual bool IsSystem { get; }
  13: }

對於用於表示認證類型的AuthenticationType屬性來說,在工作組模式下返回NTLM。對於域模式,如果操作係統是Vista或者以後的版本,該屬性返回Negotiate,表示采用SPNEGO認證協議。而對於之前的Windows版本,則該屬性值為Kerberos。

Groups屬性返回WindowsIdentity對應的Windows帳號所在的用戶組(User Group),而IsGuest則用於判斷Windows帳號是否存在於Guest用戶組中。IsSystem屬性則表示Windows帳號是否是一個係統帳號。

如果你對ASP.NET的安全有一定的了解,應該知道我們可以對IIS進行相應的配置是ASP.NET應用支持匿名用戶。也就是說,用戶無需提供具體的用戶憑證,而是以匿名的方式登錄到ASP.NET站點中。對於匿名登錄,IIS實際上會采用一個預先指定的Windows帳號進行登錄。而在這裏,IsAnonymous屬性就表示該WindowsIdentity對應的Windows帳號是否是匿名帳號。

對於匿名身份的問題,在這裏還有一點值得補充一下。WindowsIdentity定義了如下一個靜態的GetAnonymous方法用於返回一個表示匿名身份的WindowsIdentity對象。但是,並不對應著某個確定的Windows帳號。

   1: public class WindowsIdentity : IIdentity, ...
   2: {    
   3:     //其他成員
   4:     public static WindowsIdentity GetAnonymous()
   5: }

任何一個具體的Windows進程總是運行在一個確定的安全身份下。如果你手工啟動一個.exe文件,被開啟的進程會運行在基於當前登錄帳號的身份下。如有你同時擁有多個Windows帳號,你可以通過“Run As”的方式選擇一個不同於當前登錄帳號的身份去運行某個.exe文件。而對於很多的Windows服務,它們大多運行在某個係統帳號下。比如我們熟悉的IIS(IIS6或者之後的版本)在默認的情況下就運行在Network Service這個係統帳號下麵。當一個線程在這個進程中被創建並啟動的時候,進程的安全身份會自動附加到線程上。WindowsIdentity為我們提供了如下一個GetCurrent靜態方法返回基於當前線程/進程的WindowsIdentity。

   1: public class WindowsIdentity : IIdentity, ...
   2: {    
   3:     //其他成員
   4:     public static WindowsIdentity GetCurrent();
   5: }

雖然對於這些我們常用的認證類型,比如Windows認證、Forms認證和Windows Live Passport認證,都具有對應的安全身份類型。如果我們采用自定義的認證方式,是否意味著我們也需要定義一個實現了IIdentity接口的類型呢?實際上是不需要的,我們可以直接使用GenericIdentity這個類型。

正如名稱所體現的一樣,GenericIdentity為我們定義了一個一般性的安全身份。GenericIdentity的定義非常簡單,僅僅實現了定義在IIdentity接口的三個隻讀屬性而以。我們可以通過指定用戶名或者用戶名與認證類型來創建一個GenericIdentity對象。下麵的代碼片斷體現了GenericIdentity的整個定義。

   1: public class GenericIdentity : IIdentity
   2: {
   3:     public GenericIdentity(string name);
   4:     public GenericIdentity(string name, string type);
   5:  
   6:     public virtual string AuthenticationType { get; }
   7:     public virtual bool   IsAuthenticated { get; }
   8:     public virtual string Name { get; }
   9: }

由於GenericIdentity的IsAuthenticated屬性是隻讀,也不同通過存儲過程對其進行初始化,那麼如何確定一個通過GenericIdentity對象表示的安全身份是否已經通過認證了呢?實際上,GenericIdentity采用很簡單的邏輯來判斷其自身是否經過認證:如果用戶名不為空,IsAuthenticated返回True,否則返回False。下麵給出的代碼可以驗證這一點。

   1: var anonymousIdentity     = new GenericIdentity("");
   2: var authenticatedIdentity     = new GenericIdentity("Foo");
   3: Debug.Assert(anonymousIdentity.IsAuthenticated     == false);
   4: Debug.Assert(authenticatedIdentity.IsAuthenticated     == true);

通過前麵一章的介紹,我們知道了WCF具有三種典型的認證方式:Windows認證、用戶名/密碼認證和證書認證。認證的方式決定了安全身份的類型,對於Windows認證和用戶名/密碼認證,認證後的安全身份分別由一個WindowsIdentity和GenericIdentity表示。但是對於證書認證,則對應著另一種安全身份類型:X509Identity。

X509Identity定義在程序集System.IdentityModel中,對應的命名空間是System.IdentityModel.Claims。從下麵給出的定義我們可以看出X509Identity僅僅是一個(Internal)類型。

   1: internal class X509Identity : GenericIdentity, IDisposable
   2: {
   3:     //其他成員
   4:     public X509Identity(X500DistinguishedName x500DistinguishedName);
   5:     public X509Identity(X509Certificate2 certificate);
   6:    
   7:     public X509Identity Clone();
   8:     public void Dispose();
   9:     public override string Name { get; }
  10: }

X509Identity直接繼承自GenericIdentity。我們可以通過傳入一個X509Certificate2對象或者以X500DistinguishedName對象表示的證書的標識名稱來創建X509Identity。X509Identity重寫了GenericIdentity的Name屬性,最終作為名稱的返回的是證書的主題名稱和指紋的組合(分號之後具有一個空格,比如:)。X509Identity對象的AuthenticationType屬性為“X509”。

當服務安全開始的情況,服務端在經過認證之後會創建一個上下文用以存儲基於當前服務調用相關的安全相關的信息,其中就包含了代表被認證客戶端的安全身份。這個上下文被稱為服務安全上下文,通過類型ServiceSecurityContext表示。

   1: public class ServiceSecurityContext
   2: {
   3:     //其他成員
   4:     public static ServiceSecurityContext Current { get; }
   5:     public IIdentity PrimaryIdentity { get; }
   6:     public WindowsIdentity WindowsIdentity { get; }
   7:  
   8:     public bool IsAnonymous { get; }
   9:     public static ServiceSecurityContext Anonymous { get; }
  10: }

你可以通過兩種方式獲取當前的ServiceSecurityContext,一種是通過ServiceSecurityContext的靜態隻讀屬性Current,另一種則是通過當前OperationContext的ServiceSecurityContext屬性。實際上通過這兩種方式得到的是同一個ServiceSecurityContext。ServiceSecurityContext對象的同一性可以通過下麵的代碼來驗證。

   1: var securityContext1 = OperationContext.Current.ServiceSecurityContext;
   2: var securityContext2 = ServiceSecurityContext.Current;
   3: Debug.Assert(object.ReferenceEquals(securityContext1, securityContext2));

ServiceSecurityContext具有兩個表示安全身份的屬性PrimaryIdentity和WindowsIdentity,它們都代表當前客戶端的身份。。不過需要注意的是,這是所說的Windows認證實際上包括如下三種情況:

  • 客戶端憑證為Windows憑證;
  • 客戶端憑證為用戶名/密碼憑證,並采用Windows認證模式;
  • 客戶端憑證為X.509證書憑證,並允許與Windows帳號進行映射。

而對於不屬於上述三種情況下的非Windows憑證,當前ServiceSecurityContext的WindowsIdentity屬性返回Null,而PrimaryIdentity屬性則因客戶端憑證類型和認證方式有所區別。具體來說,如果客戶端憑證為用戶名/密碼憑證,並采用Membership和Custom認證模式,則在成功認證的情況下PrimaryIdentity的屬性返回一個以用戶名作為名稱的GenericIdentity。如果客戶端憑證為X.509證書憑證,但不采用Windows帳號映射機製,則PrimaryIdentity的屬性返回的是一個X509Identity。

對於匿名客戶端(客戶端憑證類型為None),PrimaryIdentity返回的是一個空的GenericIdentity,IsAnonymous返回True。你通過靜態屬性Anonymous可以返回一個匿名ServiceSecurityContext。下滿的表格體現了成功認證後當前ServiceSecurityContext的PrimaryIdentity與客戶端憑證類型以及認證模式之間的關係。

image

從兩個重要的概念談起:Identity與Principal[上篇]
從兩個重要的概念談起:Identity與Principal[下篇]


作者:蔣金楠
微信公眾賬號:大內老A
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識別二維碼)關注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁麵明顯位置給出原文連接,否則保留追究法律責任的權利。
原文鏈接

最後更新:2017-10-26 16:34:16

  上一篇:go  通過添加HTTP Header實現上下文數據在WCF的自動傳遞
  下一篇:go  [WCF權限控製]從兩個重要的概念談起:Identity與Principal[下篇]