兩種RBAC權限控制模型詳解

序言

因爲最近一直卡在權限控制這個坎上,原來設計的比較簡單的權限控制思路已經沒法知足比較複雜一些的場景,所以一直在探索一種在大部分場景下比較通用的權限模型。html

首先,這裏說明一下兩種RBAC權限模型分別是「基於角色的權限控制(Role-Based-Access-Control)」和「基於資源的權限控制(Resource-Based-Access-Control)」兩種模型,這兩種模型是Java最多見的權限控制的模型。它們之間的數據庫結構區別並無太大,甚至也能夠同樣。都是以最基礎的五張表(用戶表、角色表、用戶-角色關係表、權限表、角色-權限關係表)組成,日後再複雜的業務再在這個基礎上進行拓展,例如加入用戶組、組織、模塊等等概念,能夠參考一下一這篇文章:java

https://blog.csdn.net/qiaqia609/article/details/38102091
數據庫

下面就以Spring Boot + Shiro爲載體來具體記錄一下。apache

1.準備數據庫

首先仍是最基礎的五張表,後續的全部需求都是在這上面來拓展的,不論是基於角色仍是基於資源都是差很少的數據結構。

這裏仍是貼一下Shiro的配置文件:ShiroConfig.javaapi

  
  
  
  
  1. /**
  2. * @author phw
  3. * @date Created in 04-08-2018
  4. * @description 基於RESTFul風格的Shiro配置
  5. */
  6. @Slf4j
  7. @Configuration
  8. public class ShiroConfig {
  9. @Bean
  10. public DefaultWebSecurityManager securityManager(MyShiroRealm myShiroRealm) {
  11. DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
  12. securityManager.setRealm(myShiroRealm);
  13. /**
  14. * 關閉shiro自帶的session管理
  15. * http://shiro.apache.org/session-management.html#SessionManagement-StatelessApplications%28Sessionless%29
  16. */
  17. DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
  18. DefaultSessionStorageEvaluator evaluator = new DefaultSessionStorageEvaluator();
  19. evaluator.setSessionStorageEnabled( false);
  20. subjectDAO.setSessionStorageEvaluator(evaluator);
  21. securityManager.setSubjectDAO(subjectDAO);
  22. return securityManager;
  23. }
  24. @Bean
  25. public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) {
  26. ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
  27. //filterFactoryBean.setLoginUrl("/sign-in");
  28. //添加本身的過濾器並取名jwt
  29. Map<String, Filter> filterMap = new HashMap<>();
  30. filterMap.put( "jwt", new JWTFilter());
  31. filterFactoryBean.setFilters(filterMap);
  32. filterFactoryBean.setSecurityManager(securityManager);
  33. filterFactoryBean.setUnauthorizedUrl( "/401");
  34. //自定義url規則
  35. Map<String, String> filterRuleMap = new LinkedHashMap<>();
  36. // 訪問401和404頁面不經過咱們的Filter
  37. /*filterRuleMap.put("/401", "anon");
  38. filterRuleMap.put("/403", "anon");
  39. filterRuleMap.put("/404", "anon");
  40. filterRuleMap.put("/sign-in", "anon");
  41. filterRuleMap.put("/sign-up", "anon");
  42. filterRuleMap.put("/sign-out", "logout");
  43. // 全部請求經過本身的JWT Filter
  44. filterRuleMap.put("/**", "jwt");*/
  45. /*List<Permission> permissions = permissionMapper.selectAll();
  46. for (Permission permission: permissions) {
  47. filterRuleMap.put(permission.getUrl(), permission.getInit());
  48. }
  49. filterFactoryBean.setFilterChainDefinitionMap(filterRuleMap);
  50. log.info("Shiro Filter Factory Bean inject successful...");*/
  51. return filterFactoryBean;
  52. }
  53. @Bean
  54. @DependsOn( "lifecycleBeanPostProcessor")
  55. public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
  56. DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
  57. // 強制使用cglib,防止重複代理和可能引發代理出錯的問題
  58. // https://zhuanlan.zhihu.com/p/29161098
  59. advisorAutoProxyCreator.setProxyTargetClass( true);
  60. return advisorAutoProxyCreator;
  61. }
  62. @Bean
  63. public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
  64. return new LifecycleBeanPostProcessor();
  65. }
  66. @Bean
  67. public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
  68. AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
  69. advisor.setSecurityManager(securityManager);
  70. return advisor;
  71. }
  72. }

這裏配置的是無狀態(stateless)的shiro配置,由於要用到RESTful協議,因此須要使用jwt來保證api的安全。因此這裏須要關閉掉Shiro自帶的Session管理器,而後啓用Shiro註解,自定義URL規則會在稍後說到。安全

2.Role Based Access Control

首先,來講一下比較常見的基於角色的權限控制。session

咱們所說的角色,其實就是一系列權限的集合,裏面指定了哪一種角色能夠作什麼事情,而用戶也能夠看做是一組角色的集合,因此咱們在使用第一種rbac來進行權限控制的時候,通常是判斷一個用戶是否有某一個角色。咱們用Shiro來說就是像下面這樣:數據結構

  
  
  
  
  1. @RequiresRoles( "admin")
  2. //或者
  3. if(SecurityUtils.getSubject().hasRole( "admin")) {
  4.      //do some thing...
  5.     System.out.println( "I'm admin.")
  6. }

上面的代碼是不少文章集成shiro後成功的樣子,我當時也是這麼作的,可是隨着後來權限管理的業務愈來愈複雜,這種簡單的或者說靜態的權限管理模型已經不適用了,舉個栗子:若是如今我要增長一個角色,名字叫teacher,讓它也能打印一點東西。那麼要作的就是首先在數據庫添加角色名,而後修改權限代碼,改爲:app

  
  
  
  
@RequiresRoles(value = {"admin", "teacher"}, logical = Logical.OR)

或者相似。也就是說,每一次變動需求,都須要變動代碼,而後從新部署項目~~這顯然是不怎麼符合咱們預期要求的。less

所以這種模型只適合對權限需求變更不大的場景了。

固然,優勢就是很是簡單,開發起來使用起來很是方面,特別是配合上Shiro,一個註解就能搞定。

3.Resource Based Access Control

第二種基於資源的權限控制,是第一種的優化方案,咱們仍是來看看栗子:

  
  
  
  
  1. @RequiresPermissions( "user:add")
  2. //或者
  3. if(SecurityUtils.getSubject().hasPermission( "user:add")) {
  4. //do some thing...
  5. System.out.println( "user:add");
  6. }

這種方式在必定程度上解決了上述第一種模型的問題,網上也有不少關於Resource Based Access Control的解釋,可是這裏我用我本身的話來解釋一下,仍是用上面的栗子,客戶要求「admin」和「teacher」都有權限去打印一些東西。因而,第二種就不須要再修改代碼了,我直接在前臺給teacher受權「user:add」這個權限就能夠了啊,由於這樣已經很明確的指定了執行這個方法須要什麼權限,就是「user:add」權限,我如今無論你是什麼角色,什麼組織,什麼模塊也好,只要你擁有這個「user:add」這個權限,那你就能執行這個方法。

可是,問題又來了。

若是如今需求變動,執行這個方法須要另一個權限「user:edit」怎麼辦?是否是又得像上面那樣,修改代碼,從新部署項目。

因此,咱們還須要一種可以徹底動態控制權限的模型,不把任何權限或者角色寫死,直接在數據庫(前臺)配置,每增長一個功能就註冊一個功能而後指定它的須要的權限,就算需求變動,也不須要從新部署項目,只須要在前臺稍微修改配置一下就能達到目的。

太晚了,今天就寫到這裏吧,預知後事如何,請聽下回.......分解........

原文地址:https://blog.csdn.net/qq_33698579/article/details/80159823
相關文章
相關標籤/搜索