1. 程式人生 > >許可權管理系統(五):RBAC新解,基於資源的許可權管理

許可權管理系統(五):RBAC新解,基於資源的許可權管理

本文討論以角色概念進行的許可權管理策略及主要以基於角色的機制進行許可權管理是遠遠不夠的。同時我將討論一種我認為更好的許可權管理方式。

1、什麼是角色

當說到程式的許可權管理時,人們往往想到角色這一概念。角色是代表一系列可執行的操作或責任的實體,用於限定你在軟體系統中能做什麼、不能做什麼。使用者帳號往往與角色相關聯,因此,一個使用者在軟體系統中能做什麼取決於與之關聯的各個角色。

例如,一個使用者以關聯了”專案管理員”角色的帳號登入系統,那這個使用者就可以做專案管理員能做的所有事情――如列出專案中的應用、管理專案組成員、產生專案報表等。

從這個意義上來說,角色更多的是一種行為的概念:它表示使用者能在系統中進行的操作集合

2、基於角色的訪問控制(Role-Based Access Control)

既然角色代表了可執行的操作這一概念,一個合乎邏輯的做法是在軟體開發中使用角色來控制對軟體功能和資料的訪問。你可能已經猜到,這種許可權控制方法就叫基於角色的訪問控制(Role-Based Access Control),或簡稱為RBAC。

有兩種正在實踐中使用的RBAC訪問控制方式:隱式(模糊)的方式和顯示(明確)的方式。

今天依舊有大量的軟體應用是使用隱式的訪問控制方式。但我肯定的說,顯示的訪問控制方式更適合於當前的軟體應用。

3、隱式的訪問控制

前面提到,角色代表一系列的可執行的操作。但我們如何知道一個角色到底關聯了哪些可執行的操作呢?

答案是:目前的大多數應用,你並能不明確的知道一個角色到底關聯了哪些可執行操作。可能你心裡是清楚的(你知道一個有”管理員”角色的使用者可以鎖定使用者帳號、進行系統配置;一個關聯了”消費者”這一角色的使用者可在網站上進行商品選購),但這些系統並沒有明確定義一個角色到底包含了哪些可執行的行為。

拿”專案管理員”來說,系統中並沒有對”專案管理員”能進行什麼樣的操作進行明確定義,它僅是一個字串名詞。開發人員通常將這個名詞寫在程式裡以進行訪問控制。例如,判斷一個使用者是否能檢視專案報表,程式設計師可能會編碼如下:

程式碼塊1.隱式地基於角色的許可權控制:

if (user.hasRole("Project Manager")) {
    
//show the project report button } else { //don't show the button }

在上面的示例代表中,開發人員判斷使用者是否有”專案管理員”角色來決定是否顯示檢視專案報表按鈕。請注意上面的程式碼,它並沒有明確語句來定義”專案管理員”這一角色到底包含哪些可執行的行為,它只是假設一個關聯了專案管理員角色的使用者可檢視專案報表,而開發人員也是基於這一假設來寫if/else語句。

4、脆弱的許可權策略

像上面的許可權訪問控制是非常脆弱的。一個極小的許可權方面的需求的變動都可能導致上面的程式碼需要重新修改。

舉例來說,假如某一天這個開發團隊被告知:“哦,順便說一下,我們需要一個’部門管理員’角色,他們也可以檢視專案報表。請做到這一點。”

這種情況下,開發人員需要找到上面的程式碼塊並將其修改為:

程式碼塊2.修改過的隱式的基於角色的許可權控制:

if (user.hasRole("Project Manager") || user.hasRole("Department Manager") ) {
   //show the project report button
} else {
   //don't show the button
}

隨後,開發人員需要更新他的測試用例、重新編譯系統,還可能需要重走軟體質量控制(QA)流程,然後再重新部署上線。這一切僅僅是因為一個微小的許可權方面的需求變動!

後面如果需求方又回來告訴你說我們又有另一個角色可檢視報表,或是前面關於”部門管理員可檢視報表”的需求不再需要了,豈不把人累死了。

如果需求方要求動態地建立、刪除角色以便他們自己配置角色,又該如何應對呢?

像上面的情況,這種隱式的(靜態字串)形式的基於角色的訪問控制方式難以滿足需求。理想的情況是如果許可權需求變動不需要修改任何程式碼。怎樣才能做到這一點呢?

5、顯式地訪問控制:更好的選擇

從上面的例子我們看到,當權限需求發生變動時,隱式的許可權訪問控制方式會給程式開發帶來沉重的負擔。如果能有一種方式在許可權需求發生變化時不需要去修改程式碼就能滿足需求那就好了。理解的情況是,即使是正在執行的系統,你也可以修改許可權策略卻又不影響終端使用者的使用。當你發現某些錯誤的或危險的安全策略時,你可以迅速地修改策略配置,同時你的系統還能正常使用,而不需要重構程式碼重新部署系統。

怎樣才能達到上面的理想效果呢?我們可以通過顯式的(明確的)界定我們在應用中能做的操作來進行。

回顧上面隱式的許可權控制的例子,思考一下這些程式碼最終的目的,想一下它們最終是要做什麼樣的控制?

從根本上說,這些程式碼最終是在保護資源(專案報表),是要界定一個使用者能對這些資源進行什麼樣的操作(檢視/修改)。當我們將許可權訪問控制分解到這種最原始的層次,我們就可以用一種更細粒度(更富有彈性)的方式來表達許可權控制策略。

我們可以修改上面的程式碼塊,以基於資源的語義來更有效地進行許可權訪問控制:

程式碼塊3.顯式的許可權控制:

if (user.isPermitted("projectReport:view:12345")) {
   //show the project report button
} else {
   //don't show the button
}

上面的例子中,我們可明確地看到我們是在控制什麼。不要太在意冒號分隔的語法,這僅是一個例子,重點是上面的語句明確地表示了“如果當前使用者允許檢視編號為12345的專案報表,則顯示專案報表按鈕”。也就是說,我們明確地說明了一個使用者帳號可對一個的資源例項進行的具體的操作。

6、為什麼說這種方式更好

上面最後的示例程式碼塊與前面的程式碼的主要區別:最後的程式碼塊是基於什麼是受保護的, 而不是誰可能有能力做什麼。看似簡單的區別,但後者對系統開發及部署有著深刻的影響:

  • 更少的程式碼重構:我們是基於系統的功能(系統的資源及對資源的操作)來進行許可權控制,而相應來說,系統的功能需求一旦確定下來後,一段時間內對它的改動相應還是比較少的。只是當系統的功能需求改變時,才會涉及到許可權程式碼的改變。例如上面提到的檢視專案報表的功能,顯式的許可權控制方式不會像傳統隱式的RBAC許可權控制那樣因不同的使用者/角色要進行這個操作就需要重構程式碼;只要這個功能存在,顯式的方式的許可權控制程式碼是不需要改變的。
  • 更直觀:保護資源物件、控制對資源物件的操作(物件及物件的行為),這樣的許可權控制方式更符合人們的思想習慣。正因為符合這種直觀的思維方式,面向物件的編輯思想及REST通訊模型變得非常成功。
  • 更有彈性:上面的示例程式碼中沒有寫死哪些使用者、組或角色可對資源進行什麼操作。這意味著它可支援任何安全模型的設計。例如,可以將操作(許可權)直接分配給使用者 ,或者他們可以被分配到一個角色,然後再將角色與使用者關聯,或者將多個角色關聯到組(group)上,等等。你完全可以根據應用的特點定製許可權模型。
  • 外部安全策略管理:由於原始碼只反映資源和行為,而不是使用者、組和角色,這樣資源/行為與使用者、組、角色的關聯可以通過外部的模組或專用工具或管理控制檯來完成。這意味著在許可權需求變化時,開發人員並不需要花費時間來修改程式碼,業務分析師甚至終端使用者就可以通過相應的管理工具修改許可權策略配置。
  • 可在執行環境做修改:因為基於資源的許可權控制程式碼並不依賴於行為的主體(如組、角色、用色),你並沒有將行為的主體的字元名詞寫在程式碼中,所以你甚至可以在程式執行的時候通過修改主體能對資源進行的操作這樣一些方式,通過配置的方式就可應對許可權方面需求的變動,再也不需要像隱式的RBAC方式那樣需要重構程式碼。

7、RBAC新解:Resource-Based Access Control

對於上面列出的諸多好處,我重點要說是這種顯式的機制帶給我們的富有彈性的許可權模型。

如果你仍想保留或模擬傳統的基於角色的許可權訪問控制,你可以將許可權(可執行的操作)直接分配給某個角色。這種情況下,你依舊是在使用基於角色的許可權訪問控制方式(不同之處在於你需要明確地界定角色中的許可權,而不是傳統的使用角色字串隱式地進行許可權控制)。

但在這種新的模型下,已不必再侷限於角色了。你可以將許可權直接分配給使用者、組或其它你覺得可以的物件。

因為上面顯式地、基於資源的許可權訪問控制的諸多好處,或許可以給RBAC一個新的定義:“Resource-Based Access Control”。

8、現實世界的例子:Apache Shiro

如果你好奇現實世界有沒有被多個系統使用的基於資源的許可權控制框架,你可以瞭解一下Apache Shiro。它是一個java平臺的現代許可權管理框架。通過它的許可權(Permission)概念,Shiro很好地支援基於資源的許可權訪問控制。

當然,並不需要借用Shiro來理解本文所說的一些概念,但Shiro對理解本文所討論的概念及示例有一定的幫助。