基於角色訪問控制RBAC權限模型的動態資源訪問權限管理實現

RBAC權限模型(Role-Based Access Control)

前面主要介紹了元數據管理和業務數據的處理,一般一個系統都會有多個用戶,不一樣用戶具備不一樣的權限,本文主要介紹基於RBAC動態權限管理在crudapi中的實現。java

概要

RBAC簡介

RBAC權限模型(Role-Based Access Control)即:基於角色的權限控制。模型中有幾個關鍵的術語:
用戶:系統接口及訪問的操做者
權限:可以訪問某接口或者作某操做的受權資格
角色:具備一類相同操做權限的用戶的總稱 api

用戶角色權限關係

一個用戶有一個或多個角色
一個角色包含多個用戶
一個角色有多種權限
一個權限屬於多個角色 安全

Spring security

Spring Security是Spring項目組中用來提供安全認證服務的框架,能夠很方便的實現動態權限管理。框架

表單配置

系統內置5個表單,這些表單和權限相關,和具體業務無關ide

資源resource

resource
其中url是ANT格式表達式,用於配置url來肯定是否擁有某個資源的權限。測試

用戶user

user
用戶表記錄登陸用戶信息this

角色role

role
角色url

用戶角色行userRoleLine

userRoleLine
用戶和角色的中間表,參考以前表關係管理,利用兩個一對多創建多對多關係,3d

角色資源行roleResourceLine

roleResourceLine
角色和資源的中間表,一樣的利用兩個一對多創建多對多關係 code

表關係

relation

原表 目標表 關係
user userRoleLine 一對多
userRoleLine role 多對一
role roleResourceLine 一對多
roleResourceLine resource 多對一

權限控制原理

根據登陸用戶首選獲取角色列表,每一個角色對應多個資源,最終用戶的權限爲多個角色對應的資源疊加。若是擁有某個資源權限就返回數據,不然提示無權限。
默認若是沒有匹配任何資源,表示該資源無需特別權限,只須要登陸用戶便可。

驗證

customerResource
添加客戶資源,ANT url爲/api/business/customer/*,操做爲,表示GET,PATCH,DELETE,POST都須要受權。
若是操做爲DELETE,表示值控制DELETE操做,其它操做不限制。

noAuth
經過UI訪問客戶時候提示沒有權限,和指望的效果一致

addRole
添加角色「客戶管理員」,該角色擁有客戶訪問權限

addRoleLine
給「超級管理員」添加「客戶管理員」角色,這樣「超級管理員」就擁有了客戶訪問權限

customerOK
由於用戶從新分配了角色,須要須要註銷從新登陸,登陸以後又能夠正常訪問客戶資源了。

核心源碼

@Slf4j
@Component
public class DynamicSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {

    private static Map<String, ConfigAttribute> configAttributeMap = null;

    @Autowired
    private DynamicSecurityService dynamicSecurityService;

    @PostConstruct
    public void loadDataSource() {
        configAttributeMap = dynamicSecurityService.loadDataSource();
    }

    public void clearDataSource() {
        log.info("DynamicSecurityMetadataSource clearDataSource");
        configAttributeMap.clear();
        configAttributeMap = null;
    }

    @Override
    public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException {
        if (configAttributeMap == null)  {
            this.loadDataSource();
        }
        List<ConfigAttribute>  configAttributes = new ArrayList<>();

        FilterInvocation fi = (FilterInvocation) o;

        String method = fi.getRequest().getMethod();
        log.info("getAttributes method = " + method);

        //獲取當前訪問的路徑
        String url = fi.getRequestUrl();
        String path = URLUtil.getPath(url) + "_"+ method;

        log.info("getAttributes url = " + url);
        log.info("getAttributes path = " + path);

        PathMatcher pathMatcher = new AntPathMatcher();
        Iterator<String> iterator = configAttributeMap.keySet().iterator();
        //獲取訪問該路徑所需資源
        while (iterator.hasNext()) {
            String pattern = iterator.next();
            if (pathMatcher.match(pattern, path)) {
                log.info("match success = " + pattern + ", " + path);
                configAttributes.add(configAttributeMap.get(pattern));
            }
        }
        // 未設置操做請求權限,返回空集合
        return configAttributes;
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }

}

繼承FilterInvocationSecurityMetadataSource,實現getAttributes接口,經過http url加http method進行匹配

@Slf4j
@Component
public class DynamicAccessDecisionManager implements AccessDecisionManager {

    @Override
    public void decide(Authentication authentication, Object object,
                       Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
        // 當接口未被配置資源時直接放行
        if (CollUtil.isEmpty(configAttributes)) {
            log.info("empty configAttributes decide passed!");
            return;
        }

        FilterInvocation fi = (FilterInvocation) object;

        String method = fi.getRequest().getMethod();
        log.info("decide method = " + method);

        List<String> needAuthorityList = new ArrayList<String>();

        Iterator<ConfigAttribute> iterator = configAttributes.iterator();
        while (iterator.hasNext()) {
            ConfigAttribute configAttribute = iterator.next();
            //將訪問所需資源或用戶擁有資源進行比對
            String needAuthority = configAttribute.getAttribute();
            needAuthorityList.add(needAuthority);
            for (GrantedAuthority grantedAuthority : authentication.getAuthorities()) {
                if (needAuthority.trim().equals(grantedAuthority.getAuthority())) {
                    return;
                }
            }
        }
        throw new AccessDeniedException("對不起,您沒有資源:" + String.join(",", needAuthorityList) +"的訪問權限!");
    }

    @Override
    public boolean supports(ConfigAttribute configAttribute) {
        return true;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }
}

繼承AccessDecisionManager,實現decide接口,將訪問所需資源或用戶擁有資源進行比對,若是擁有權限則放行,不然提示無權限。

小結

本文介紹了RBAC在crudapi中的實現原理,首先引入Spring security框架,而後利用配置生成用戶,角色,資源等表單,經過配置實現基本的CRUD功能,最終實現了動態權限精細化管理。由於用戶,角色等表與業務無關,因此會做爲系統內置表單。

附demo演示

本系統屬於產品級的零代碼平臺,不一樣於自動代碼生成器,不須要生成Controller、Service、Repository、Entity等業務代碼,程序運行起來就能夠使用,真正0代碼,能夠覆蓋基本的和業務無關的CRUD RESTful API。

官網地址:https://crudapi.cn
測試地址:https://demo.crudapi.cn/crudapi/login

相關文章
相關標籤/搜索