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
其中url是ANT格式表達式竹宋,用于配置url來確定是否擁有某個資源的權(quán)限劳澄。
用戶user
用戶表記錄登錄用戶信息
角色role
角色
用戶角色行userRoleLine
用戶和角色的中間表,參考之前表關(guān)系管理蜈七,利用兩個一對多建立多對多關(guān)系秒拔,
角色資源行roleResourceLine
角色和資源的中間表,同樣的利用兩個一對多建立多對多關(guān)系
表關(guān)系
原表 | 目標表 | 關(guān)系 |
---|---|---|
user | userRoleLine | 一對多 |
userRoleLine | role | 多對一 |
role | roleResourceLine | 一對多 |
roleResourceLine | resource | 多對一 |
權(quán)限控制原理
根據(jù)登錄用戶首選獲取角色列表飒硅,每個角色對應多個資源砂缩,最終用戶的權(quán)限為多個角色對應的資源疊加。如果擁有某個資源權(quán)限就返回數(shù)據(jù)三娩,否則提示無權(quán)限庵芭。
默認如果沒有匹配任何資源,表示該資源無需特別權(quán)限雀监,只需要登錄用戶即可双吆。
驗證
添加客戶資源,ANT url為/api/business/customer/*会前,操作為好乐,表示GET,PATCH瓦宜,DELETE蔚万,POST都需要授權(quán)。
如果操作為DELETE临庇,表示值控制DELETE操作笛坦,其它操作不限制区转。
通過UI訪問客戶時候提示沒有權(quán)限,和期望的效果一致
添加角色“客戶管理員”版扩,該角色擁有客戶訪問權(quán)限
給“超級管理員”添加“客戶管理員”角色,這樣“超級管理員”就擁有了客戶訪問權(quán)限
因為用戶重新分配了角色侄泽,需要需要注銷重新登錄礁芦,登錄之后又可以正常訪問客戶資源了。
核心源碼
@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