基于角色訪問控制RBAC權(quán)限模型的動態(tài)資源訪問權(quán)限管理實現(xiàn)

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

前面主要介紹了元數(shù)據(jù)管理和業(yè)務數(shù)據(jù)的處理,通常一個系統(tǒng)都會有多個用戶碰辅,不同用戶具有不同的權(quán)限领猾,本文主要介紹基于RBAC動態(tài)權(quán)限管理在crudapi中的實現(xiàn)瑰妄。

概要

RBAC簡介

RBAC權(quán)限模型(Role-Based Access Control)即:基于角色的權(quán)限控制陷嘴。模型中有幾個關(guān)鍵的術(shù)語:
用戶:系統(tǒng)接口及訪問的操作者
權(quán)限:能夠訪問某接口或者做某操作的授權(quán)資格
角色:具有一類相同操作權(quán)限的用戶的總稱

用戶角色權(quán)限關(guān)系

一個用戶有一個或多個角色
一個角色包含多個用戶
一個角色有多種權(quán)限
一個權(quán)限屬于多個角色

Spring security

Spring Security是Spring項目組中用來提供安全認證服務的框架,可以很方便的實現(xiàn)動態(tài)權(quán)限管理间坐。

表單配置

系統(tǒng)內(nèi)置5個表單灾挨,這些表單和權(quán)限相關(guān),和具體業(yè)務無關(guān)

資源resource

resource

其中url是ANT格式表達式竹宋,用于配置url來確定是否擁有某個資源的權(quán)限劳澄。

用戶user

user

用戶表記錄登錄用戶信息

角色role

role

角色

用戶角色行userRoleLine

userRoleLine

用戶和角色的中間表,參考之前表關(guān)系管理蜈七,利用兩個一對多建立多對多關(guān)系秒拔,

角色資源行roleResourceLine

roleResourceLine

角色和資源的中間表,同樣的利用兩個一對多建立多對多關(guān)系

表關(guān)系

relation
原表 目標表 關(guān)系
user userRoleLine 一對多
userRoleLine role 多對一
role roleResourceLine 一對多
roleResourceLine resource 多對一

權(quán)限控制原理

根據(jù)登錄用戶首選獲取角色列表飒硅,每個角色對應多個資源砂缩,最終用戶的權(quán)限為多個角色對應的資源疊加。如果擁有某個資源權(quán)限就返回數(shù)據(jù)三娩,否則提示無權(quán)限庵芭。
默認如果沒有匹配任何資源,表示該資源無需特別權(quán)限雀监,只需要登錄用戶即可双吆。

驗證

customerResource

添加客戶資源,ANT url為/api/business/customer/*会前,操作為好乐,表示GET,PATCH瓦宜,DELETE蔚万,POST都需要授權(quán)。
如果操作為DELETE临庇,表示值控制DELETE操作笛坦,其它操作不限制区转。

noAuth

通過UI訪問客戶時候提示沒有權(quán)限,和期望的效果一致

addRole

添加角色“客戶管理員”版扩,該角色擁有客戶訪問權(quán)限

addRoleLine

給“超級管理員”添加“客戶管理員”角色,這樣“超級管理員”就擁有了客戶訪問權(quán)限

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));
            }
        }
        // 未設置操作請求權(quán)限悼尾,返回空集合
        return configAttributes;
    }

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

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

}

繼承FilterInvocationSecurityMetadataSource柿扣,實現(xiàn)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) +"的訪問權(quán)限!");
    }

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

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

繼承AccessDecisionManager析桥,實現(xiàn)decide接口司草,將訪問所需資源或用戶擁有資源進行比對,如果擁有權(quán)限則放行泡仗,否則提示無權(quán)限埋虹。

小結(jié)

本文介紹了RBAC在crudapi中的實現(xiàn)原理,首先引入Spring security框架娩怎,然后利用配置生成用戶搔课,角色,資源等表單截亦,通過配置實現(xiàn)基本的CRUD功能爬泥,最終實現(xiàn)了動態(tài)權(quán)限精細化管理。因為用戶崩瓤,角色等表與業(yè)務無關(guān)袍啡,所以會作為系統(tǒng)內(nèi)置表單。

附demo演示

本系統(tǒng)屬于產(chǎn)品級的零代碼平臺谷遂,不同于自動代碼生成器葬馋,不需要生成Controller、Service肾扰、Repository畴嘶、Entity等業(yè)務代碼,程序運行起來就可以使用集晚,真正0代碼窗悯,可以覆蓋基本的和業(yè)務無關(guān)的CRUD RESTful API。

官網(wǎng)地址:https://crudapi.cn
測試地址:https://demo.crudapi.cn/crudapi/login

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末偷拔,一起剝皮案震驚了整個濱河市蒋院,隨后出現(xiàn)的幾起案子亏钩,更是在濱河造成了極大的恐慌,老刑警劉巖欺旧,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件姑丑,死亡現(xiàn)場離奇詭異,居然都是意外死亡辞友,警方通過查閱死者的電腦和手機栅哀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來称龙,“玉大人留拾,你說我怎么就攤上這事■曜穑” “怎么了痴柔?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長疫向。 經(jīng)常有香客問我咳蔚,道長,這世上最難降的妖魔是什么鸿捧? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任屹篓,我火速辦了婚禮肚医,結(jié)果婚禮上条舔,老公的妹妹穿的比我還像新娘。我一直安慰自己牍颈,他們只是感情好泼菌,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布谍肤。 她就那樣靜靜地躺著,像睡著了一般哗伯。 火紅的嫁衣襯著肌膚如雪荒揣。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天焊刹,我揣著相機與錄音系任,去河邊找鬼。 笑死虐块,一個胖子當著我的面吹牛俩滥,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播贺奠,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼霜旧,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了儡率?” 一聲冷哼從身側(cè)響起挂据,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤以清,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后崎逃,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體掷倔,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年婚脱,在試婚紗的時候發(fā)現(xiàn)自己被綠了今魔。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡障贸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出吟宦,到底是詐尸還是另有隱情篮洁,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布殃姓,位于F島的核電站袁波,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏蜗侈。R本人自食惡果不足惜篷牌,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望踏幻。 院中可真熱鬧枷颊,春花似錦、人聲如沸该面。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽隔缀。三九已至题造,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間猾瘸,已是汗流浹背界赔。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留牵触,地道東北人淮悼。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像荒吏,于是被迫代替她去往敵國和親敛惊。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容