Role
角色模式
王翔 (Vision Wang)
2009-02-16
分類
資訊安全結構型模式
動機、問題、影響因素
相信您和我一樣,從進入託兒所、上學、工作這20多年的過程中經常需要學習各種規章制度,不過不知道您注意到沒有,區別於通知中關於表揚/批評的內容,這些規章制度往往不是針對個人的。
當然,您也許第一時間跳出來說,“怎麼會?我們企業的規章就說了,報銷超過2000元的差旅費必須經過X總批准才可以。”,如果真是這樣,那沒有辦法,我們也要遵守。
我們看兩個例子,希望可以勾回您的回憶:
規章
所有本科生最多每次借閱3本書,2樓人文圖書最多借閱1本
通知
電腦系08級本科張三同學在本學期《資料結構》考試中夾帶。
經教務處確認,該同學本次考試成績無效,給予嚴重警告處分一次,留校察看,以觀後效。
上面兩個例子從物件導向看,前者是某個規則的定義,後者是另一個規則的執行個體。對於我們的應用而言,開發階段我們的主要目標往往是把各種規則表述為代碼(或類似的機制),然後使用者在執行的過程中會在具體上下文中選擇適用不同的規則。
以授權規則為例,回顧一下我們之前的項目經驗,項目中多使用者的許可權管理往往並不簡單,但典型的業務系統往往如上面的樣本,他的商務規則往往是基於組織機構以及機構中崗位設定的,而人員作為個體是通過成長,在不同崗位間流動,在不同崗位完成不同的工作。通過分析,我們不難發現,在原來直接關係的“使用者——操作功能”之間如果引入一個第三方對象,也許可以有效隔絕上述變化帶來的影響。它既可以把“一捆”功能打包,也可以把“一批”使用者打包,但無論如何我們明確一個目標——將一組具有相似抽象特徵的內容作第二次抽象。
解決方案
角色模式經典解決方案抽象如下:
“建立一系列名為‘角色’的對象,用它抽象一組使用者的存取權限”。
他與傳統方式直接授權的區別如下:
圖 04-01:引入Role前後的授權結構示意
更大規模、更正常化企業環境角色模式的處理情景
上面提到的處理方式轉換就是角色模式(Role(s) Pattern)的主要目標,他最普遍的應用情景是授權。但對於一些大型項目、超大型項目而言,如果企業業務管理制度明確、崗位定義清晰,項目功能繁多,這樣一層的Role也許還不夠,例如:
l 企業機構即便在地區總部也有4、5層的層級關係,人員崗位的等級就更複雜了;
l 地區機構人員雖然僅1000多人,但因為都是白領人員,因此工作機構、部門職能設定比較複雜;
l 項目是個比較大型的ERP,大致有14000多個功能介面;
l 授權人員歸口一個部門主管,但實際授權人員只有2、3個人;
l 人員部門間交流的情況比較普遍;
相信面對這樣的情況,如果僅僅作一層抽象,可能我們的授權人員也忙不過來,那麼根據需要不妨作第二層、第三層抽象。例如,下面是個方案:
l 將人員“打包”的Role稱為崗位,相同機構層次上有不同的崗位。例如:機構為“開發部”,崗位有:
n 助理開發工程師
n 開發工程師
n 進階開發工程師
n 設計師
n 架構師
n 部門副經理
n 部門經理
n 開發總監
n …
l 將功能“打包”的Role稱為許可權;例如:項目方案審批許可權,包括下列功能:
n 瀏覽需求分析書
n 瀏覽使用者使用習慣調查報告
n 項目財務預算登記、修改、上報
n …
這樣,14000多個功能可能就合并為7、8十個許可權,1000多人合并為40多個崗位,授權工作量減少了1、2個量級。
圖 04-02:多層角色模式的應用情景
更多幹擾維度授權處理措施
上面說的其實只是“常規”授權,但現實中授權往往還同時受到多個維度幹擾,關於授權相信您也有大把的經驗可以分享,去除簡單的“使用者——角色——功能”外,我們可能還要面臨非常多的問題:
l 授權往往與屬地關聯在一起,而且隨著國際化、地區的特點,授權往往需要增加一個維度——“管轄範圍”,也就是說您可以審批單證,但能審批單證屬地、目標地為何處呢?
l 另外,授權往往也和業務資料關聯,例如:您可以審批報銷,但是否可以審批金額超過2萬元的單子呢?即便您可以定義一個“大金額報銷審批”的角色,是不是就夠了,畢竟有可能5天以後我們覺得2萬Euro元才是大金額,難道還要再定義一個角色?
l 授權是否有反操作?例如:雖然“出納助理”都可以做一些工作,但對於新人可能暫時要“卡”掉個別功能,您是否也考慮再定義一個角色,還是增加一個授權方向的反向操作;
l 相信您對“秘書”的作用也有瞭解,秘書能做的往往不僅自己的那些許可權,她/他們常常可以代理高層的工作,但我們的高層是否喜歡她/他們掌握自己的帳號/密碼呢?還是說僅僅在某些情況下把自己“分內”的某些功能分給她/他,這裡就涉及如何對於這種具有“委託”關係的內容進行管理;
l 如何讓企業不同層次的授權人員可以在一個更加簡單的“視圖”上看到他的授權相關的組織機構。例如:財務部門很多,總部的、地區總部的、分公司的,但某批功能就是針對財務開發的,他們授權的時候就只看到自己轄域範圍的即可,而且不需要看到其他部門;
l 人員部門間交流普遍,甚至經常出現整個部門的倒換、裁撤。如何令授權人員快速完成類似的“批量”授權;
l 機構間是否有絕對的垂直領導作用,例如:同樣是複查審批,總部人員是否可以直接審批地區總部的每票單證呢?
l … …
說實在的,具體項目中解決任何一個問題雖然方法很多,但都並不輕鬆。主要原因是我們除了要提供功能外,還要盡量避免出現“牽一髮、動全身”的被動局面。參考我們在本系列開篇提到解決的思路:
l 編織(weaving)多層AOP設計;
l 採用橋甚至是我們在《設計模式--基於C#的工程化實現及擴充》GOF23經典部分介紹的那樣,藉助多級橋模式解決;
AOP方式的邏輯示意如下:
圖 04-03:採用AOP方式實現具有多個變化維度幹擾的角色模式應用情境
圖 04-04:採用多級橋模式實現具有多個變化維度幹擾的角色模式應用情境
樣本
下面,我們還原角色模式最本源的狀態,看一個完全定製的具體樣本:
實現
抽象介面
C#
/// 授權體系的基本介面
public interface ISecurityObject
{
string Name { get; }
}
/// 功能
public interface IFunction : ISecurityObject
{
}
/// 角色
public interface IRole : ISecurityObject
{
IEnumerable<IFunction> Functions { get; set; }
/// 配置
void Config(IEnumerable<IFunction> functions);
}
/// 帳號
public interface IAccount : ISecurityObject
{
IEnumerable<IRole> Roles { get; }
/// 授權檢查
bool IsInRole(string roleName);
/// 授權檢查
bool CouldOperateFunction(string functionName);
/// 授權(正向)
void Grant(IRole role);
/// 授權(反向)
void Revoke(IRole role);
}
樣本實體類型
C#
class SecurityObjectMock : ISecurityObject
{
public string Name { get; set; }
}
class FunctionMock : SecurityObjectMock, IFunction { }
class RoleMock : SecurityObjectMock, IRole
{
IEnumerable<IFunction> functions;
public IEnumerable<IFunction> Functions
{
get { return this.functions; }
set { this.functions = value; }
}
public void Config(IEnumerable<IFunction> functions)
{
this.functions = functions;
}
}
class AccountMock : SecurityObjectMock, IAccount
{
IList<IRole> roles = new List<IRole>();
public IEnumerable<IRole> Roles { get { return roles; } }
public bool IsInRole(string roleName)
{
if (string.IsNullOrEmpty(roleName))
throw new ArgumentNullException("roleName");
return
(from role in this.Roles
where string.Equals(role.Name, roleName)
select role)
.Count() > 0 ? true : false;
}
public bool CouldOperateFunction(string functionName)
{
if (string.IsNullOrEmpty(functionName))
throw new ArgumentNullException("functionName");
return
(from role in this.Roles
from func in role.Functions
where string.Equals(func.Name, functionName)
select func)
.Count() > 0 ? true : false;
}
public void Grant(IRole role)
{
if (role == null) throw new ArgumentNullException("role");
roles.Add(role);
}
public void Revoke(IRole role)
{
if (role == null) throw new ArgumentNullException("role");
roles.Remove(role);
}
}
單元測試
C#
[TestMethod]
public void TestAuthorizationWithSimpleRole()
{
// 配置授權系統內容
IRole role1 = new RoleMock()
{
Name = "A",
Functions = new List<IFunction>(){
new FunctionMock(){Name = "A1"},
new FunctionMock(){Name = "A2"},
new FunctionMock(){Name = "A3"}
}
};
IRole role2 = new RoleMock()
{
Name = "B",
Functions = new List<IFunction>(){
new FunctionMock(){Name = "B1"},
new FunctionMock(){Name = "B2"}
}
};
// 執行個體化使用者
IAccount account = new AccountMock();
// 使用者授權及許可權檢查
account.Grant(role1);
account.Grant(role2);
Assert.IsTrue(account.IsInRole("A"));
Assert.IsTrue(account.IsInRole("B"));
Assert.IsTrue(account.CouldOperateFunction("A2"));
Assert.IsTrue(account.CouldOperateFunction("B2"));
// 調整授權及許可權檢查
account.Revoke(role1);
Assert.IsFalse(account.IsInRole("A"));
Assert.IsTrue(account.IsInRole("B"));
Assert.IsFalse(account.CouldOperateFunction("A2"));
Assert.IsTrue(account.CouldOperateFunction("B1"));
Assert.IsTrue(account.CouldOperateFunction("B2"));
}
結果分析
上面的樣本不難看出,如果將“角色”、“功能”等僅僅泛化為字元傳名稱,藉助現有的開發手段實現一個定製的授權檢查功能其邏輯部分並不複雜。
但如果如上面樣本,IFunction、IRole、IAccount抽象了更加複雜的行為,比如:關注授權主體、授權對象、授權上下文等多個方面的時候,藉助角色模式這個對象化的體系,就可以賦予系統更多的靈活性,尤其相對於平時通過“單純資料庫 + SQL/預存程序”的那個方案,因為任何一個實體類型,可能都會包括比一個名稱豐富的多的資訊與控制。
相關模式
如上,如果角色模式不是面向經典設計中那樣,是關於功能的“打包”,而是關於使用者主體的“打包”,那麼我們會經常藉助組合模式和迭代器模式向使用者提供更簡單的授權功能,例如:使用者要授予一個地區中心及下屬單位所有使用者“看公司年報”的功能,那麼可以直接把這個地區授與相關的功能,那麼各級崗位的人員根據組合和迭代過程都可以操作該功能,而無需授權管理員費盡辛苦的為每個崗位都作一下授權;
另外,有些時候,我們的授權可能不總是針對角色,就如我們在本系列開篇說的,可能還有Identity Based Security,或者是Identity Based Security作為補充機制的需要,這時候也需要充分結合組合模式與迭代器模式的特徵,解決不同層次顆粒度的授權問題。
如果您之前瀏覽過《設計模式--基於C#的工程化實現及擴充》GOF23經典部分的介紹,相信您在閱讀上面樣本的同時也已經明顯的感覺到組合模式的特徵,而迭代器則是用LINQ完成的,其實上面樣本之所以增加一個ISecurityObject也是為這裡討論的情境預留退路。
如上面提到的,我們還可能經常用到橋模式解決多維授權變化及管控因素影響的情況;
具體項目中,設計安全機制的過程中,往往還會結合上一章節的檢查點模式,實現業務授權的同時,盡量堵住“非法嘗試”的過程。
行業案例
我們使用的Windows是個非常典型的Role Based Security + Identity Based Security系統;
QQ群、MSN Group、GTalk Group都是在典型C2C形式IM工具基礎上,擴充角色模式,實現“群”互動的典型例子。
更多關註:
《設計模式——基於C#的工程化實現及擴充》 Security Design Pattern 系列 1 公開金鑰體系與分布式環境要求
《設計模式--基於C#的工程化實現及擴充》 Security Design Pattern 系列 3 檢查點模式(Check Point)
關於《設計模式——基於C#的工程化實現及擴充》書籤
關於《設計模式——基於C#的工程化實現及擴充》封面
關於《設計模式——基於C#的工程化實現與擴充》電子書、範例程式碼發布,互動網預訂開始
關於《設計模式——基於C#的工程化實現及擴充》定價修改
關於“我的第一次策劃實踐”
關於我的第一次策劃實踐——初入目錄分析之門
幫你造就有彈性、能擴充、易維護的軟體實體
“王翔——設計模式C#工程化實現”線上講座資料下載
《設計模式》的表達模式
圍爐取暖話“創業&升職”,請看《走出軟體作坊》;
圍爐取暖話“求職&面試”,請看《編程之美——微軟技術面試心得》