這是給我們學校的某管理系統設計的一套權限管理子系統。今天把它總結一下,歡迎大牛過來踩。
權限系統的設計對於企業級項目來說極爲重要,尤其是像我們的系統,數據很重要,要是成績被人修改了,那就糟糕了。
其實權限控制無非是兩方面:控制合適的人訪問到合適的菜單,進入合適的菜單項之後可以訪問到合適的數據。
我把系統設計的儘量簡化,主要牽涉到以下幾個對象。
一個是權限所有者Accessor,即訪問者
Accessor可以使一個用戶,也可以是一個角色或是崗位,是什麼只有客戶程序知道。
一個是資源,Resource,表示可被訪問的數據,
爲樹形結構,每個資源爲一個樹的節點。客戶程序可以自己控制資源的粒度。
一個是功能模塊,Module,一個功能模塊,或者是一個可訪問的頁面。
由於這部分有其他人員開發,我的程序裏面沒有涉及到這個類,只是引用了這個類的ID。
一個是權限Authoritied,有三個外鍵,分別指向上面三個對象,其實就是一張三表關聯的中間表。
針對以上所述,對程序敏感的你可能會提出以下問題:
1. Accessor可以是一個用戶,也可以是一個角色或是崗位,那麼如何表示一個訪問者呢?
我用了AccessorName這個字段,它是一個字符串類型,而且是不可重複的,由於我們的系統裏表都有uuid,所以就把uuid放進來,作爲AccessorName了。這裏有一點,客戶程序不只需用告訴我訪問者的AccessorName是什麼,並不需要知道這個Accessor是什麼類型,所以這裏的處理是可行的。
2. 這裏的資源可以是各種類型,那麼如何抽象的表示出各種類型的資源呢?
這個問題和上面的類似,但處理方法不能一樣,因爲客戶程序查詢可訪問資源列表之後需要知道資源的類型。處理的方法是,用Class類型的ResourceClass字段表示資源的類型,用ResourceId表示這個類型下的某個ID,通過這兩個字段就可以確定資源是哪個具體的類型的實例了。
3. Authorited只是一張關聯表,只能表示有沒有訪問的權限,不能表示權限的等級,比如讀權限/寫權限,那麼如何控制權限等級呢?
我是這麼處理的,把菜單看做是一個功能模塊,把菜單項看做是資源,而菜單細化到具體的功能,由於我們的系統裏面每個頁面的功能是相對單一的,沒有把各種身份的功能做到一起,所以每一個菜單項就可以表示權限的等級了,比如查看成績是一個菜單項,修改成績是另外一個菜單項,這樣只要控制這個人能否訪問到指定頁面就可以控制到權限等級。
以上是解決顯示合適的菜單項的方案,當訪問者進入合適的菜單項之後再查詢可訪問的資源列表,於是顯示合適的數據。由於資源是樹形結構,客戶程序還可以查詢某以某個資源實例爲父資源的某種類型的資源,這樣很容易控制數據的級聯菜單形式的現實。
接下來就看一下我對外提供的操作接口方法是怎麼樣的吧。
public interface AclController {
/**
* 查詢權限,返回的資源列表中的每一項都遞歸的包含父資源直到Root
* @param accessor 訪問者
* @param moduleId 訪問的模塊ID
* @return 可訪問的資源列表
* @throws AclDatabaseAccessException
* @throws AclAnnotationConfigurationException
* @author xuke
*/
public List<Resource> get(Accessor accessor, Long moduleId) throws AclDatabaseAccessException, AclAnnotationConfigurationException;
/**
* 查詢權限,返回的資源列表中的每一項都不包含父資源
* @param accessor 訪問者
* @param moduleId 訪問的模塊ID
* @param parent 父資源
* @param resourceClazz 父資源Class對象
* @return
* @throws AclDatabaseAccessException
* @throws AclAnnotationConfigurationException
* @author xuke
*/
public List<Resource> get(Accessor accessor, Long moduleId, Resource parent, Class resourceClazz) throws AclDatabaseAccessException, AclAnnotationConfigurationException;
/**
* 修改一個權限
* @param oldAuthoritied
* @param newAuthoritied
* @throws AclDatabaseAccessException
* @throws AclAnnotationConfigurationException
* @author xuke
*/
public void modify(Authoritied oldAuthoritied, Authoritied newAuthoritied) throws AclDatabaseAccessException, AclAnnotationConfigurationException;
/**
* 獲取根資源
* @return
* @throws AclDatabaseAccessException
* @throws AclAnnotationConfigurationException
* @author xuke
*/
public Resource getRootResource() throws AclDatabaseAccessException, AclAnnotationConfigurationException;
/**
* 增加一個資源
* @param resource
* @throws AclDatabaseAccessException
* @throws AclAnnotationConfigurationException
* @author xuke
*/
public void add(Resource resource) throws AclDatabaseAccessException, AclAnnotationConfigurationException;
/**
* 刪除一個資源
* @param resource
* @throws AclDatabaseAccessException
* @throws AclAnnotationConfigurationException
* @author xuke
*/
public void delete(Resource resource) throws AclDatabaseAccessException, AclAnnotationConfigurationException;
/**
* 修改一個資源
* @param resource
* @throws AclDatabaseAccessException
* @throws AclAnnotationConfigurationException
* @author xuke
*/
public void modify(Resource resource) throws AclDatabaseAccessException, AclAnnotationConfigurationException;
/**
* 增加一個權限主體
* @param accessor
* @throws AclDatabaseAccessException
* @throws AclAnnotationConfigurationException
* @author xuke
*/
public void add(Accessor accessor) throws AclDatabaseAccessException, AclAnnotationConfigurationException;
/**
* 刪除一個權限主體
* @param accessor
* @throws AclDatabaseAccessException
* @throws AclAnnotationConfigurationException
* @author xuke
*/
public void delete(Accessor accessor) throws AclDatabaseAccessException, AclAnnotationConfigurationException;
/**
* 修改一個權限主體
* @param accessor
* @throws AclDatabaseAccessException
* @throws AclAnnotationConfigurationException
* @author xuke
*/
public void modify(Accessor accessor) throws AclDatabaseAccessException, AclAnnotationConfigurationException;
/**
* 添加一個權限
* @param authoritied
* @throws AclDatabaseAccessException
* @throws AclAnnotationConfigurationException
* @author xuke
*/
public void add(Authoritied authoritied) throws AclDatabaseAccessException, AclAnnotationConfigurationException;
/**
* 刪除一個資源
* @param authoritied
* @throws AclDatabaseAccessException
* @throws AclAnnotationConfigurationException
* @author xuke
*/
public void delete(Authoritied authoritied) throws AclDatabaseAccessException, AclAnnotationConfigurationException;
}
其實權限系統主要是設計,至於如何實現都是小事情,簡單說一下把,我的這個模塊裏是用spring做ioc的,數據庫操作方面考慮到效率問題所以是用的jdbc,但模仿了hibernate的Annotation的配置方式,自己實現了表和字段的映射,所以sql是自動生成的,如果數據庫改了,sql是不需要改的。
有興趣的可以看看我的整個的代碼,歡迎大牛給出寶貴意見,小弟先受了。