文章目錄
發布時間:2007-07-24
適用於:軟體架構師
摘要:本文通過樣本和概念解釋,闡述了公司資訊化軟體中許可權設計的一般方法,協助架構師能夠快速的建立客戶所需的安全模型。
本文內容
公司資訊化軟體許可權設計概述
現狀
從一個例子開始
在ERP中運用這些概念
確定主體
定義可用的認證
授權
安全檢查
關於拒絕功能
關於ShareTo功能
總結
公司資訊化軟體的許可權設計一直是個非常棘手的工作,因為在公司資訊化軟體中,需求總是千奇百怪,很難有一個統一的方案,能夠既滿足複雜的許可權需求,又能夠保證程式的高效能和易維護性。
現狀
在設計許可權系統時,一般有兩種取向,一種是試圖做一種“全面解決方案”,例如可以對單條的文檔進行獨立的使用權限設定,或者設計出讓人眼花繚亂的設定介面;另外一種傾向是定製化的許可權系統,根據不同客戶的管理思路,建立特有的許可權模型。例如有些企業希望以管轄範圍劃分許可權,而有些希望以單據的客戶層級劃分。
第一種方案問題在於使用了抽象化的概念描述許可權,從而使使用者操作複雜煩瑣,而且必然帶來程式執行的緩慢。第二種方案使用者理解方便,執行效率高,但定製化的成本高昂,且無法處理非常複雜的許可權需求。
在本篇文章中,我將描述偏向第二種方案的設計,但更強調定製化需求抽象化出來的理論,以及反過來根據此理論簡化許可權模型的設計。
從一個例子開始
首先從一個生活中簡單的例子開始,我們將從生活中學習到如何設計許可權。
一家運動俱樂部,可以購買兩種會員卡,一個是金卡,一個是銀卡,金卡呢,當然是可以玩進階的裝置,還有個VIP包廂,至於銀卡你就只能到大廳玩了。
現在某人購買了一張金卡,跑到VIP包廂門口,對門口的服務員說:俺要進去,服務員檢查了金卡,想了想可不可以去包廂(億分之一秒完成此思考),通過,進去吧。
好了,我們開始“理論化”一下這個故事。
資源:是指需要保護的內容,在這個例子中,大廳和包廂就是兩種資源,資源套件含一些屬性,以便判斷是否可以使用的依據,例如大廳和包廂都包含一個層級的屬性,這些屬性本身並不直接描述使用許可權。在ERP中,例如訂單、客戶等等都是資源。
認證:有的稱為憑證,是指可以被管理方承認的憑證,在這個俱樂部中,金卡和銀卡都是被承認的認證,當然,別的俱樂部的金卡是不被承認的(除非業務更複雜些),在ERP中,常見的認證有:角色、登入人Id、所在部門、職位等等都可以作為認證。
主體:指試圖訪問資源的發起人,在這個例子裡,那個客戶就是一個主體,一個主體可以擁有一個或多個的認證,在ERP中,一個登入人員就可能擁有多個角色認證。
授權:是指資源的管理方事先定義的一組可訪問的規則,例如剛才說的金卡可以玩VIP包廂就是一個由俱樂部經理事先定義的規則。一份授權應該描述為:
主體必須具有哪些認證,才能對哪些資源執行哪些動作。
在上面的話中,有一個詞:動作,在這個例子中動作指:用。正如你所想像的,俱樂部經理是沒有授權金卡客戶可以砸包廂。J
在ERP軟體中,我們經常看見的使用權限設定介面就是典型的授權定義過程。
最終,當某個主體試圖訪問某些資源時,管理方根據主體持有的認證,在授權定義中最終枚舉出可訪問的資源。
在ERP中運用這些概念確定主體
通常在ERP中主體的確認非常的簡單,都是當前登入的使用者,一般都設計成ERP獨立的驗證過程,在.NET中需要自己定義System.Security.Principal.IPrincipal介面的實作類別來描述主體,如果程式很簡單你可以直接使用System.Security.Principal.GenericPrincipal。
某些ERP軟體整合Windows驗證,也支援ERP軟體的自身驗證,一般我們會在ERP自己的賬戶中定義一個映射的Windows賬戶,當使用整合驗證時,通過當前的Windows賬戶找到ERP的賬戶,最終以ERP的賬戶統一作為主體。
還有一種情況就是後台自動服務,例如某些大型的ERP會包含自己的後台服務,他定期執行諸如報表產生的操作,這種程式可以定義諸如LocalService這樣的特殊賬戶作為主體。你可以在Windows的服務中看見這種例子。
在.NET中,主體包含了標識對象的引用,但沒有直接包含認證的定義。
下面是在.NET中設定標識對象的簡單例子:
Console.WriteLine("Login...");
Console.Write("user name:");
string userName = Console.ReadLine();
GenericIdentity currentUser = new GenericIdentity(userName);
GenericIdentity是.NET內建的一個簡單標識對象,.NET中關於主體還提供了WindowsPrincipal,以及Web中的System.Web.Security.RolePrincipal。
定義可用的認證
要在ERP運用這些概念,你首先需要歸納主體所有可用的認證,即可以被管理方承認的憑證種類。例如在下面的例子中:
財務經理可以查看所有員工的工資
在這個例子中,“財務經理”,這個角色就是一個被認可的認證,即角色是一種認證。
每個員工可以查看自己的工資
此例中,“員工”,這個人員檔案也是一種被認可的認證,有些軟體將登入的操作員檔案和員工檔案分離,那麼此操作員映射的員工就是此操作員的認證。因此,員工也是一種認證。
地區經理可以查看他分管地區的銷售單
地區經理在登入時,他所分管的地區定義也將成為他的認證,這是一種典型的與特定業務掛鈎的認證。所以根據不同的業務需求,可能會定義除角色和員工之外的更多認證,他們都將在使用者登入後,寄生在主體對象上一起放入會話中。
當然,最常見的認證還是角色,作為習慣,或者說作為好的設計,我們都會設計以下特殊的角色:
角色
描述
Everyone
表示任何人,不管你是否有賬戶,不需要指定都屬於此角色;
Guest
表示任何尚未登入的人,在登入後自動丟失這個角色認證;
User
表示普通使用者,沒有任何強制性規則,一般會在建立一個新使用者時自動附加;
Administrator
好東西,開發人員都懂
Backup Operator
很多設計人員忽略了他,其實這個角色很適合IT維護人員。
在.NET中,認證是放在主體中一起存放的,例如下面的例子中描述了如何將users和administrators兩個角色放在主體中。
GenericPrincipal p = new GenericPrincipal(
currentUser,
new string[] { "users", "administrators" });
System.Threading.Thread.CurrentPrincipal = p;
一般的,在ERP中,你需要自己定義主體對象,他是符合IPrincipal介面的對象,以便按照你自己的方式儲存各種類型的認證。
授權
授權至少包含以下資訊:
l 此授權要求的認證,可以是一個或多個,通常要求的認證是一個角色認證;
l 此授權被授予到哪些實體上,這屬於安全性原則的一部分,例如僅應用於訂單,但也可以是一個模組,例如可以訪問整個銷售系統;
l 此授權被授予的動作,仍然屬於安全性原則的一部分,動作一般包括可見、唯讀、建立、更新、刪除和生效等。
l 最後就是安全性原則的特定化條件,例如:僅是自己的訂單,表示:where OrderSheet.SheetId = @CurrentUserId;
可以看出,授權包含兩個重要的部分:所需認證和安全性原則。
授權中第一個部分是所需認證,下面的介面清楚的表明了授權和認證的關係:
授權的第二個部分包括了功能的安全性原則和資料的安全性原則,功能安全性原則指對“功能”這個資源的訪問授權,資料安全性原則指對資料這個資源的訪問授權。
表示了在這個授權中關於功能的安全性原則定義。
關於資料的安全性原則要看具體的業務實體設計,例如,訂單實體支援客戶欄位,所以就能夠支援客戶層級的安全授權方式,而員工檔案就沒有客戶欄位,也就不可能支援客戶層級的安全授權方式了。
一般的,常見的資料授權方式有:
l 實體包含所有者(Owner)屬性,“所有者”是一個員工檔案(或者是映射一個員工檔案),表示此記錄的持有人,與記錄的資料本身沒有直接關係,可以參考Microsoft CRM。
l 實體包含員工屬性,和所有者相似,但和實際資料有關,例如工資單,許可權是跟此工資單的工資人有關,比較多見於HR系統和辦公自動化系統;
l 實體包含客戶屬性,按照客戶的所屬地區或者層級授權,多見於CRM或進銷存系統;
l 實體包含所屬部門屬性,和所有者概念很相似,不同的是一個掛靠員工,一個掛靠部門,例如一個發貨單包含發貨倉庫屬性,一個生產計劃單包含一個車間屬性。多見於ERP系統。
下面的介面是按照管轄範圍定義的資料授權。
一個授權只能是一種授權方式,一般我們在建立授權時就已經決定了。
提示:最佳的資料授權方式是動態資料的授權方式,而不是具體資料的授權,例如的:操作員直接管轄範圍,這種授權使管理員不用為每個經理單獨設定授權,大大減少管理的工作量。
一個系統中允許添加很多的授權。很多軟體錯誤的將授權定義到角色上,這將大大限制授權定義的靈活性。這個問題大家可以參考Windows的設計,你注意到了嗎?Windows的角色(使用者組)編輯介面中是沒有任何使用權限設定的部分。
.NET提供了描述安全性原則的介面:System.Security.IPermission,這個介面定義了安全性原則的以下行為:
方法
解釋
Copy
建立並返回當前許可權的相同副本
Demand
斷言,如果不滿足安全要求,則會在運行時引發 SecurityException
Intersect
建立並返回一個許可權,該許可權是當前許可權和指定許可權的交集
IsSubsetOf
確定當前許可權是否為指定許可權的子集
Union
建立一個許可權,該許可權是當前許可權與指定許可權的並集
這個介面還派生自System.Security.ISecurityEncodable,他定義使權限物件狀態與 XML 元素表示形式進行相互轉換的方法。
要實現這個介面老實說不是那麼容易的事情,但更不幸的是這個東西根據不同業務需求還要定義不同的實現,看看.NET自己實現的類你就知道這個就是“工作量”了。
注意:恕我愚鈍,我沒有在.NET中表示授權的對象,只有他的子部分:安全性原則。所以我是自己實現這個類的。
安全檢查
做了這麼多,終究是為了最後一個事情:安全檢查。
首先在登入時,我們需要在主體上記錄所有可用的認證。
當某個方法被調用時,方法內部首先就需要做安全檢查,比較簡單的安全檢查是:斷言,他的結果只有:通過或者不通過。如果不通過,一般會拋出異常。
這種檢查一般適用於:
l 檢查建立文檔的許可權;
l 檢查對某個具體文檔的操作;
另外一種是對資料的過濾,這種操作沒有事先確定的資料,例如瀏覽就是典型的資料過濾方式的安全檢查。
這兩種方式的流程如下:
l 過濾出所有可用的授權,即目前使用者擁有授權中要求的認證,即為可用授權;
l 再次過濾,保留當前操作的功能和動作在授權範圍內的;
l 如果有任何一個安全性原則表明為“最大集”,就認為最大許可權;
l 將剩餘授權中安全性原則按照授權方式產生並集,每種授權方式形成一個並集;
l 如果不存在任何有效授權,表示沒有許可權;
兩種安全檢查的最後一步流程不同,如果是斷言方式,則:
l 將當前的資來源物件傳入安全性原則中,檢查此資來源物件是否在安全性原則範圍之內,例如下面描述了一個訂單在讀取時的資料資源屬性:
屬性名稱
值
資料Id
00002
所有者
User001
對應客戶分管地區
001
如果定義的安全性原則中描述為:處理自己的管轄的地區,而目前使用者管轄的就是001,則通過。
如果是資料過濾方式,則
l 將所有的並集以OR的方式拼接到執行的SQL中。
關於拒絕功能
在上面的所有授權定義中,都是正向授權,也有一種反向授權,即拒絕功能,這個功能可以參看NTFS系統的許可權設計。
反向授權就是將正向授權的內容又“踢”出去,這在處理一些特殊的許可權非常有用,例如原本授權財務可以編製本公司的所有工資單,可現在請過來的經理不歸他管,這樣就需要做一個正向授權:財務角色可以編輯本公司的工資單,然後做一個反向授權,加入那個經理。
關於ShareTo功能
我們可以看到,授權被定義在一個獨立的文檔中,但是在遇到那種規律性非常差的業務需求時,這種方式遇到了挑戰。
因為缺乏規律性,所以不得不製作大量的獨立授權,獨立授權即那種單獨給某個角色甚至某個使用者的授權,隨著授權的增多,程式變得緩慢無比,而且,由於授權是個敏感性資料,所以只能由管理員製作,這勢必增加了系統管理的工作量。
作為補充,有些系統設計了ShareTo功能,他的特點是授權被掛接到某個具體條目,例如是一個關於Account的ShareTo的資料庫設計。
第一條ShareTo記錄表明:Account中,記錄編號為069111…的資料,允許User1讀取。
這種設計實際上是在統一的授權外,又為這個檔案單獨建立了一個授權模型,將過多的獨立授權集中到某幾個檔案上,好處是顯而易見的,犧牲自己,解放大家嗎。
使用ShareTo功能,你必須謹記以下幾點:
l ShareTo功能帶來靈活性,但同時帶來了運行緩慢、管理無序;
l ShareTo必須依託在某個記錄上,所以是沒有Create這個動作的;
l 設計ShareTo功能必須考慮再次ShareTo問題,一般的我們都會在ShareTo中增加“再授權”動作。
總結
應該記住,世界上沒有包治百病的靈丹妙藥,任何一種許可權設計模型都必須在效能、靈活性和可用性做出平衡。
在抉擇使用那種安全模型時,經常犯的錯誤是“誇大”客戶的需求和資料量,從而總是試圖給客戶一個大而全的許可權系統,其實合適就好。