[WCF權限控製]從兩個重要的概念談起:Identity與Principal[下篇]
毫不誇張地說,安全主體(Principal)是整個授權機製的核心。我們可以簡單地將將安全主體定義成能夠被成功實施授權的主體。一個安全主體具有兩個基本的要素:基於某個用戶的安全身份和該用戶具有的權限。絕大部分的授權都是圍繞著“角色”進行的,我們將一組相關的權限集和一個角色綁定,然後分配給某個用戶。所以在基於角色授權環境下,我們可以簡單地將安全主體表示成:身份 + 角色。在.NET基於安全的應用編程接口中,通過IPrincipal接口表示安全主體。
目錄
一、IPrincipal
二、WindowsPrincipal
三、GenericPrincipal
四、 基於安全主體的授權
用以表示安全主體的IPrincipal接口定義在System.Security.Principal命名空間下。IPrincipal的定義體現在如下的代碼片斷中,從中我們可以看出IPrincipal僅僅具有兩個成員。隻讀屬性Identity表示安全主體的身份,而IsInRole用以判斷安全主體對應的用戶是否被分配了給定的角色。
1: public interface IPrincipal
2: {
3: bool IsInRole(string role);
4: IIdentity Identity { get; }
5: }
上麵我們具體介紹了IIdentity接口的兩個實現,WindowsIdentity和GenericIdentity。實際上IPrincipal也具有相類似的實現類型:WindowsPrincipal和GenericPrincipal,它們均定義在System.Security.Principal命名空間下。
我們先來談談WindowsPrincipal。之前我們談到一個安全主體具有身份與權限兩個基本要素,在Windows安全體係下,某個用戶具有的權限決定於它被添加到那些用戶組(User Group)中。Windows默認為我們創建了一些用戶組,比如Adminstrators和Guests等。你也根據需要創建自定義用戶組。從本質上講,Windows的用戶組和我們之前談到的角色並沒有本質的區別,都是一組權限的載體。
WindowsPrincipal的定義如下。表示安全身份的隻讀屬性Identity返回一個WindowsIdentity對象,該對象在WindowsPrincipal被創建的時候通過構造函數指定。所以在Windows安全體係,一個用戶組具有多種不同的標識方式,比如相對標識符(RID:Relative Identifier)、安全標識符(SID:Security Identifier)和用戶組名稱,對於一些與定義的用戶組甚至還可以通過System.Security.Principal.WindowsBuiltInRole枚舉來表示,所以WindowsPrincipal具有若幹重載的IsInRole方法。
1: public class WindowsPrincipal : IPrincipal
2: {
3: public WindowsPrincipal(WindowsIdentity ntIdentity);
4: public virtual bool IsInRole(int rid);
5: public virtual bool IsInRole(SecurityIdentifier sid);
6: public virtual bool IsInRole(WindowsBuiltInRole role);
7: public virtual bool IsInRole(string role);
8: public virtual IIdentity Identity { get; }
9: }
而一個GenericPrincipal對象本質上就是對一個IIdentity對象和表示角色列表的字符創數組的封裝而以。下麵的代碼片斷體現了整個GenericPrincipal的定義。
1: public class GenericPrincipal : IPrincipal
2: {
3: public GenericPrincipal(IIdentity identity, string[] roles);
4: public virtual bool IsInRole(string role);
5: public virtual IIdentity Identity { get; }
6: }
一個通過接口IPrincipal表示的安全主體不僅僅可以表示被授權用戶的身份(通過Identity屬性),其本身就具有授權判斷的能力(通過IsInRole方法)。如果我們在訪問者成功實施認證後根據用戶的權限設置構建一個安全主體對象,並將其存儲在當前的上下文中,在需要的時候就可以改安全主體獲取出來以完成對授權的實現。
實際上Windows授權機製的實現就是安全這樣的原理實現的,而這個所謂的上下文就是當前線程的線程本地存儲(TLS:Thread Local Storage)。而反映在編程上,你可以通過Thread類型的CurrentPrincipal屬性來獲取或者設置這個當前的安全主體。
1: public sealed class Thread
2: {
3: //其他成員
4: public static IPrincipal CurrentPrincipal { get; set; }
5: }
一旦為當前線程設置了安全主體,在需要確定當前用戶是否有權限執行某項操作或者訪問某個資源的時候,就可以通過上述的這個CurrentPrincipal屬性將設置的安全主體獲取出來,通過調用IsInRole方法判斷當前用戶是否具有相應的權限。下麵的代碼體現了用戶需要具有Administrators角色(或者Windows用戶組)才能執行被授權的操作,否則會拋出一個安全異常。
1: IPrincipal currentPrincipal = Thread.CurrentPrincipal;
2: if (currentPrincipal.IsInRole("Administrators"))
3: {
4: //執行被授權的操作
5: }
6: else
7: {
8: //拋出安全異常
9: }
我們通過編寫具體授權邏輯的編程方式稱為。如果一個針對某個方法的授權(當前用戶是否有權限調用需要被授權的方法),我們還可以省卻所有授權代碼,采用一種。聲明式的授權需要使用到一個特殊的特性:PrincipalPermissionAttribute。
從如下代碼片斷給出的關於PrincipalPermissionAttribute類型的定義我們不難看出,這是一個與代碼訪問安全(CAS:Code Access Security)的特性(繼承自CodeAccessSecurityAttribute)。如果在某個方法上應用了該特性,授權將被以檢驗代碼訪問安全的方式來執行。PrincipalPermissionAttribute的Authenticated屬性用於指定目標方法是否一定需要在認證用戶環境下執行。而Name和Role表示執行目標方法所允許的用戶名和角色。
1: [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple=true]
2: public sealed class PrincipalPermissionAttribute : CodeAccessSecurityAttribute
3: {
4:
5: //其他成員
6: public PrincipalPermissionAttribute(SecurityAction action);
7:
8: public bool Authenticated { get; set; }
9: public string Name { get; set; }
10: public string Role { get; set; }
11: }
從應用在PrincipalPermissionAttribute上麵的AttributeUsageAttribute定義我們可以看出,該特性指定應應到類型和方法級別,並且可以在同一個目標元素上應用。如果在同一個方法上應用了不止一個PrincipalPermissionAttribute特性,那麼隻要。
在下麵的程序中,我們創建了四個應用了PrincipalPermissionAttribute特性的測試方法(TestMethod1、TestMethod2、TestMethod3和TestMethod4)。其中TestMethod1和TestMethod2上設置了不同的用戶名Foo和Bar,而TestMethod3和TestMethod4則設置了不同的角色,前者設置的單一的角色Adminstrators,後者則設置了兩個角色Adminstrators和Guests。四個訪均在Try/Catch中執行,在指定之前一個GenericPrincipal對象被創建並設置成當前線程的安全主體。該GenericPrincipal安全身份是一個用戶名為Foo的GenericIdentity,並且具有唯一的角色Guests。通過最終的輸出,我們可以看出係統自動為我們完成的授權正式采用了定義於應用在目標方法上的PrincipalPermissionAttribute特性中的授權策略。
1: static void Main(string[] args)
2: {
3: GenericIdentity identity = new GenericIdentity("Foo");
4: Thread.CurrentPrincipal = new GenericPrincipal(identity, new string[] { "Guests" });
5: Invoke(() => TestMethod1());
6: Invoke(() => TestMethod2());
7: Invoke(() => TestMethod3());
8: Invoke(() => TestMethod4());
9: }
10:
11: public static void Invoke(Action action)
12: {
13: try
14: {
15: action();
16: }
17: catch(Exception ex)
18: {
19: Console.WriteLine(ex.Message);
20: }
21: }
22:
23: [PrincipalPermission(SecurityAction.Demand, Name = "Foo")]
24: public static void TestMethod1()
25: {
26: Console.WriteLine("TestMethod1方法被成功執行。");
27: }
28: [PrincipalPermission(SecurityAction.Demand, Name = "Bar")]
29: public static void TestMethod2()
30: {
31: Console.WriteLine("TestMethod2方法被成功執行。");
32: }
33: [PrincipalPermission(SecurityAction.Demand, Role="Adminstrators")]
34: public static void TestMethod3()
35: {
36: Console.WriteLine("TestMethod3方法被成功執行。");
37: }
38: [PrincipalPermission(SecurityAction.Demand, Role = "Adminstrators")]
39: [PrincipalPermission(SecurityAction.Demand, Role = "Guests")]
40: public static void TestMethod4()
41: {
42: Console.WriteLine("TestMethod4方法被成功執行。");
43: }
輸出結果:
1: TestMethod1方法被成功執行。
2: 對主體權限的請求失敗。
3: 對主體權限的請求失敗。
4: TestMethod4方法被成功執行。
雖然從應用在PrincipalPermissionAttribute的AttributeUsageAttribute特性定義上看,PrincipalPermissionAttribute是可同時應用在類和方法上的。但是,當我們采用這個特性以聲明的方式進行WCF服務授權的時候,。
從兩個重要的概念談起:Identity與Principal[上篇]
從兩個重要的概念談起:Identity與Principal[下篇]
微信公眾賬號:大內老A
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識別二維碼)關注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁麵明顯位置給出原文連接,否則保留追究法律責任的權利。
最後更新:2017-10-26 16:34:13