一、什么是RBAC模型
RBAC模型是一個解決用戶權限問題的設計思維。
在最簡單的RBAC模型中,將用戶表設計為如下幾個表
1、用戶
2、角色
3、權限
以及這三張表衍生出來的兩張中間表
4、用戶_角色表
5、權限_角色表
上面描述的5張表就構成了最基本也是最成熟的RBAC模型,可以看得出RBAC模型中的核心是角色,所有與用戶相關的權限都是通過角色表進行關聯的!在今后我們可能會用到的按鈕元素表,樣式表等各種各樣的資源表最終都如權限表一般,與角色進行關聯。RBAC的哲學就是,認角色不認用戶,所有的查詢都是通過角色來完成的。
1.1、RBAC模型的注意點
我們之所以要用到RBAC模型,本質上是為了實現兩個功能:
1、資源對用戶的可見性,例如:當前用戶對應的角色是否能“看到“某個菜單項或是某個按鈕,這一部分其實重在前端渲染,資源的可見性並不能保證資源安全,當有心人拿到資源的請求路徑時依舊可以發出相應的請求。在我看來,對資源的可見性,更多的是對用戶的體驗上的優化。
2、對請求的鑒權,這部分才應該是我們關注的核心點。
需要注意的是,對於資源可見性的控制上,我們只要簡單的將五張表進行關聯查詢,然后通過foreach渲染出用戶可見的按鈕即可,沒有什么復雜的操作。無論是前后端分離的項目還是,傳統項目或是各種模板引擎,都建議這樣做,當然了還有別的做法shiro自帶了jsp渲染規則,大家看看就好。
而對請求鑒權時,如果我們通過手寫filter就顯得極不優雅,不便於維護。所以就需要結合一些權限框架來使用,在項目中我優先推薦使用Apache的shiro。因為大伙都能快速入手,學習曲線平穩,框架靈活又安全。
在RBAC模型中,整個系統中需要進行鑒權的菜單,按鈕,API,其實都是公共的一套,通過鑒權來實現不同角色展示不同的菜單。
二、查詢權限列表的思路
當我們在鑒權的時候,最終都是要查詢出一個collection集合,在使用mybatis的時候其實就是通過多表查詢出一個collection集合。偽代碼如下
user代表用戶表
role代表角色表
permission代表權限表
user_role代表用戶角色中間表
role_permission代表用戶權限中間表
1 @Select(select 權限字段名稱 from 2 user,user_role,role,role_permission,permission 3 where 4 user.主鍵 = user_role.用戶主鍵 5 and role.主鍵 = user_role.角色主鍵 6 and role.主鍵 = role_permission.角色主鍵 7 and permission.主鍵 = role_permission.權限主鍵 8 and user.主鍵 = ?
這樣就可以查詢出當前用戶包含的所有的權限名稱列表,shiro就是通過權限名稱來進行鑒權處理的,返回結果最好用Set<String>來接收,一個是為了放置重復,雖然說重復了也沒啥,另一個是為了在構造函數中傳入set集合。
不過這是角色構建,講道理在權限這塊,大家與查詢role套路保持一致能清晰一點點。
三、RBAC怎樣與shiro進行集成使用
我們想要將RBAC與shiro集成使用,需要優先理解,為什么要這樣做。
上面我已經提到了,在對請求進行鑒權的時候,我們碰到了一個小的難題。而shiro自定義realm繼承AuthorizingRealm抽象類,就可以優雅的解決請求鑒權。
這里有一個小彩蛋,我們可以只實現認證的realm,但是在項目中我們肯定要實現授權的realm即AuthorizingRealm,為什么授權的realm中既有認證方法又有授權方法呢?猜測是因為,在shiro中認證是鑒權的前置條件(其實好像在任何情況下都是這樣,不進行認證我都不知道用戶身份那么何談鑒權---確定當前身份有什么樣的權限嘞),所以shiro在實現鑒權realm時,並不需要繼承認證的自定義realm。 具體要怎么做的? 其實套路很簡單,就是以RABC模型中的角色為橋梁,查出當前用戶對應的所有權限,然后將每個url需要的權限進行配置(可以使xml或是@configuration)。再將構造一個SimpleAuthorizationInfo,調用public void addObjectPermissions(Collection<Permission> permissions) 將權限列表設置進當前的角色中。這樣就完成了權限的校驗。
1 //這個是shiro框架的基礎配置 2 @Configuration 3 public class ShiroConfiguration 4 { 5 //@Bean 6 //public CustomMatcher getCustomMatcher(){ 7 // return new CustomMatcher(); 8 //} 9 10 //配置自定義的Realm,這個realm是自定義的認證鑒權邏輯 11 @Bean 12 public CustomRealm getRealm() 13 { 14 CustomRealm customRealm = new CustomRealm(); 15 //customRealm.setCredentialsMatcher(customMatcher); 16 return customRealm; 17 //在realm中也可以自定義我們的密碼校驗邏輯,shiro文檔搜索 CredentialsMatcher就知道怎么玩了 18 } 19 20 //配置安全管理器 21 @Bean 22 public SecurityManager securityManager(CustomRealm realm) { 23 //使用默認的安全管理器 24 DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(realm); 25 //將自定義的realm交給安全管理器統一調度管理 26 securityManager.setRealm(realm); 27 return securityManager; 28 } 29 30 //Filter工廠,設置對應的過濾條件和跳轉條件 31 @Bean 32 public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) { 33 //1.創建shiro過濾器工廠 34 ShiroFilterFactoryBean filterFactory = new ShiroFilterFactoryBean(); 35 //2.設置安全管理器 36 filterFactory.setSecurityManager(securityManager); //3.通用配置(配置登錄頁面,登錄成功頁面,驗證未成功頁面) 37 filterFactory.setLoginUrl("/autherror?code=1"); 38 //設置登錄頁面 39 filterFactory.setUnauthorizedUrl("/autherror?code=2"); 40 //授權失敗跳轉頁面 41 //4.配置過濾器集合 42 Map<String,String> filterMap = new LinkedHashMap<String,String>(); 43 // 配置不會被攔截的鏈接 順序判斷 44 filterMap.put("/user/home", "anon"); 45 filterMap.put("/user/**", "authc"); 46 47 //5.設置過濾器 48 filterFactory.setFilterChainDefinitionMap(filterMap); return filterFactory; 49 } 50 }
四、總結
上面說了這么多,其實需要非常關注的點有兩個:
1、第一就是視圖層面我們是通過查庫以及渲染規則來完成客戶體驗層面的鑒權的,這一步無需shiro的介入(當然shiro中也有關於jsp權限渲染的內容,但是不做介紹了,自己查庫渲染比使用shiro自帶的jsp渲染要思路清晰的多,而且沒有技術壁壘,可以用於各種技術中)。
2、第二點是,在url鑒權時,通過shiro來完成,主要是通過設置filterfactory自定義權限鑒定規則,以及自定義AuthorizingRealm時注入權限列表來完成請求訪問層面的鑒權。