當說到程序的權限管理時,人們每每想到角色這一律念。角色是表明一系列可執行的操做或責任的實體,用於限定你在軟件系統中能作什麼、不能作什麼。用戶賬號每每與角色相關聯,所以,一個用戶在軟件系統中能作什麼取決於與之關聯的各個角色。java
例如,一個用戶以關聯了」項目管理員」角色的賬號登陸系統,那這個用戶就能夠作項目管理員能作的全部事情――如列出項目中的應用、管理項目組成員、產生項目報表等。程序員
從這個意義上來講,角色更多的是一種行爲的概念:它表示用戶能在系統中進行的操做。安全
既然角色表明了可執行的操做這一律念,一個合乎邏輯的作法是在軟件開發中使用角色來控制對軟件功能和數據的訪問。你可能已經猜到,這種權限控制方法就叫基於角色的訪問控制(Role-Based Access Control),或簡稱爲RBAC。框架
有兩種正在實踐中使用的RBAC訪問控制方式:隱式(模糊)的方式和顯示(明確)的方式。工具
今天依舊有大量的軟件應用是使用隱式的訪問控制方式。但我確定的說,顯示的訪問控制方式更適合於當前的軟件應用。測試
前面提到,角色表明一系列的可執行的操做。但咱們如何知道一個角色到底關聯了哪些可執行的操做呢?網站
答案是:目前的大多數應用,你並能不明確的知道一個角色到底關聯了哪些可執行操做。可能你內心是清楚的(你知道一個有」管理員」角色的用戶能夠鎖定用戶賬號、進行系統配置;一個關聯了」消費者」這一角色的用戶可在網站上進行商品選購),但這些系統並無明肯定義一個角色到底包含了哪些可執行的行爲。編碼
拿」項目管理員」來講,系統中並無對」項目管理員」能進行什麼樣的操做進行明肯定義,它僅是一個字符串名詞。開發人員一般將這個名詞寫在程序裏以進行訪問控制。例如,判斷一個用戶是否能查看項目報表,程序員可能會編碼以下:設計
代碼塊1. 隱式地基於角色的權限控制:code
if (user.hasRole("Project Manager") ) { //show the project report button } else { //don't show the button }
在上面的示例表明中,開發人員判斷用戶是否有」項目管理員」角色來決定是否顯示查看項目報表按鈕。請注意上面的代碼,它並無明確語句來定義」項目管理員」這一角色到底包含哪些可執行的行爲,它只是假設一個關聯了項目管理員角色的用戶可查看項目報表,而開發人員也是基於這一假設來寫 if/else 語句。
像上面的權限訪問控制是很是脆弱的。一個極小的權限方面的需求的變更均可能致使上面的代碼須要從新修改。
舉例來講,假如某一天這個開發團隊被告知:「哦,順便說一下,咱們須要一個’部門管理員’角色,他們也能夠查看項目報表。請作到這一點。
這種狀況下,開發人員須要找到上面的代碼塊並將其修改成:
代碼塊2. 修改過的隱式的基於角色的權限控制:
if (user.hasRole("Project Manager") || user.hasRole("Department Manager") ) { //show the project report button } else { //don't show the button }
隨後,開發人員須要更新他的測試用例、從新編譯系統,還可能須要重走軟件質量控制(QA)流程,而後再從新部署上線。這一切僅僅是由於一個微小的權限方面的需求變更!
後面若是需求方又回來告訴你說咱們又有另外一個角色可查看報表,或是前面關於」部門管理員可查看報表」的需求再也不須要了,豈不把人累死了。
若是需求方要求動態地建立、刪除角色以便他們本身配置角色,又該如何應對呢?
像上面的狀況,這種隱式的(靜態字符串)形式的基於角色的訪問控制方式難以知足需求。理想的狀況是若是權限需求變更不須要修改任何代碼。怎樣才能作到這一點呢?
從上面的例子咱們看到,當權限需求發生變更時,隱式的權限訪問控制方式會給程序開發帶來沉重的負擔。若是能有一種方式在權限需求發生變化時不須要去修改代碼就能知足需求那就行了。理解的狀況是,即便是正在運行的系統,你也能夠修改權限策略卻又不影響最終用戶的使用。當你發現某些錯誤的或危險的安全策略時,你能夠迅速地修改策略配置,同時你的系統還能正常使用,而不須要重構代碼從新部署系統。
怎樣才能達到上面的理想效果呢?咱們能夠經過顯式的(明確的)界定咱們在應用中能作的操做來進行。
回顧上面隱式的權限控制的例子,思考一下這些代碼最終的目的,想一下它們最終是要作什麼樣的控制?
從根本上說,這些代碼最終是在保護資源(項目報表),是要界定一個用戶能對這些資源進行什麼樣的操做(查看/修改)。當咱們將權限訪問控制分解到這種最原始的層次,咱們就能夠用一種更細粒度(更富有彈性)的方式來表達權限控制策略。
咱們能夠修改上面的代碼塊,以基於資源的語義來更有效地進行權限訪問控制:
代碼塊3. 顯式的權限控制:
if (user.isPermitted("projectReport:view:12345")) { //show the project report button } else { //don't show the button }
上面的例子中,咱們可明確地看到咱們是在控制什麼。不要太在乎冒號分隔的語法,這僅是一個例子,重點是上面的語句明確地表示了「若是當前用戶容許查看編號爲12345的項目報表,則顯示項目報表按鈕」。也就是說,咱們明確地說明了一個用戶賬號可對一個的資源實例進行的具體的操做
上面最後的示例代碼塊與前面的代碼的主要區別:最後的代碼塊是基於什麼是受保護的, 而不是誰可能有能力作什麼。看似簡單的區別,但後者對系統開發及部署有着深入的影響:
咱們是基於系統的功能(系統的資源及對資源的操做)來進行權限控制,而相應來講,系統的功能需求一旦肯定下來後,一段時間內對它的改動相應仍是比較少的。只是當系統的功能需求改變時,纔會涉及到權限代碼的改變。例如上面提到的查看項目報表的功能,顯式的權限控制方式不會像傳統隱式的RBAC權限控制那樣因不一樣的用戶/角色要進行這個操做就須要重構代碼;只要這個功能存在,顯式的方式的權限控制代碼是不須要改變的。
保護資源對象、控制對資源對象的操做(對象及對象的行爲),這樣的權限控制方式更符合人們的思想習慣。正由於符合這種直觀的思惟方式,面向對象的編輯思想及REST通訊模型變得很是成功。
上面的示例代碼中沒有寫死哪些用戶、組或角色可對資源進行什麼操做。這意味着它可支持任何安全模型的設計。例如,能夠將操做(權限)直接分配給用戶 ,或者他們能夠被分配到一個角色,而後再將角色與用戶關聯,或者將多個角色關聯到組(group)上,等等。你徹底能夠根據應用的特色定製權限模型
因爲源代碼只反映資源和行爲,而不是用戶、組和角色,這樣資源/行爲與用戶、組、角色的關聯能夠經過外部的模塊或專用工具或管理控制檯來完成。這意味着在權限需求變化時,開發人員並不須要花費時間來修改代碼,業務分析師甚至最終用戶就能夠經過相應的管理工具修改權限策略配置。
由於基於資源的權限控制代碼並不依賴於行爲的主體(如組、角色、用色),你並無將行爲的主體的字符名詞寫在代碼中,因此你甚至能夠在程序運行的時候經過修改主體能對資源進行的操做這樣一些方式,經過配置的方式就可應對權限方面需求的變更,不再須要像隱式的RBAC方式那樣須要重構代碼。
對於上面列出的諸多好處,我重點要說是這種顯式的機制帶給咱們的富有彈性的權限模型。
若是你仍想保留或模擬傳統的基於角色的權限訪問控制,你能夠將權限(可執行的操做)直接分配給某個角色。這種狀況下,你依舊是在使用基於角色的權限訪問控制方式(不一樣之處在於你須要明確地界定角色中的權限,而不是傳統的使用角色字符串隱式地進行權限控制)。
但在這種新的模型下,已沒必要再侷限於角色了。你能夠將權限直接分配給用戶、組或其它你以爲能夠的對象。
由於上面顯式地、基於資源的權限訪問控制的諸多好處,或許能夠給RBAC一個新的定義:「Resource-Based Access Control」。只是個人一個想法:)
若是你好奇現實世界有沒有被多個系統使用的基於資源的權限控制框架,你能夠了解一下Apache Shiro。它是一個java平臺的現代權限管理框架。經過它的權限(Permission)概念,Shiro很好地支持基於資源的權限訪問控制。
固然,並不須要借用Shiro來理解本文所說的一些概念,但Shiro對理解本文所討論的概念及示例有必定的幫助。