基于角色访问控制RBAC权限模型的动态资源访问权限管理实现
RBAC权限模型(Role-Based Access Control)
前面主要介绍了元数据管理和业务数据的处理,通常一个系统都会有多个用户,不同用户具有不同的权限,本文主要介绍基于RBAC动态权限管理在crudapi中的实现。
概要
RBAC简介
RBAC权限模型(Role-Based Access Control)即:基于角色的权限控制。模型中有几个关键的术语:
用户:系统接口及访问的操作者
权限:能够访问某接口或者做某操作的授权资格
角色:具有一类相同操作权限的用户的总称
用户角色权限关系
一个用户有一个或多个角色
一个角色包含多个用户
一个角色有多种权限
一个权限属于多个角色
Spring security
Spring Security是Spring项目组中用来提供安全认证服务的框架,可以很方便的实现动态权限管理。
表单配置
系统内置5个表单,这些表单和权限相关,和具体业务无关
资源resource

其中url是ANT格式表达式,用于配置url来确定是否拥有某个资源的权限。
用户user

用户表记录登录用户信息
角色role

角色
用户角色行userRoleLine

用户和角色的中间表,参考之前表关系管理,利用两个一对多建立多对多关系,
角色资源行roleResourceLine

角色和资源的中间表,同样的利用两个一对多建立多对多关系
表关系

| 原表 | 目标表 | 关系 |
|---|---|---|
| user | userRoleLine | 一对多 |
| userRoleLine | role | 多对一 |
| role | roleResourceLine | 一对多 |
| roleResourceLine | resource | 多对一 |
权限控制原理
根据登录用户首选获取角色列表,每个角色对应多个资源,最终用户的权限为多个角色对应的资源叠加。如果拥有某个资源权限就返回数据,否则提示无权限。
默认如果没有匹配任何资源,表示该资源无需特别权限,只需要登录用户即可。
验证

添加客户资源,ANT url为/api/business/customer/**,操作为*,表示GET,PATCH,DELETE,POST都需要授权。
如果操作为DELETE,表示值控制DELETE操作,其它操作不限制。

通过UI访问客户时候提示没有权限,和期望的效果一致

添加角色“客户管理员”,该角色拥有客户访问权限

给“超级管理员”添加“客户管理员”角色,这样“超级管理员”就拥有了客户访问权限

因为用户重新分配了角色,需要需要注销重新登录,登录之后又可以正常访问客户资源了。
核心源码
@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
基于角色访问控制RBAC权限模型的动态资源访问权限管理实现的更多相关文章
- Kubernetes角色访问控制RBAC和权限规则(Role+ClusterRole)
文章转载自:https://blog.csdn.net/BigData_Mining/article/details/88849696 基于角色的访问控制(Role-Based Access Cont ...
- 基于角色访问控制的OA系统的设计与实现
摘要:随着电子政务的快速发展和全面普及,办公自动化(OA)系统的安全性显得越来越重要.对基于Web 的B/S 结构的OA 系统结构和安全需求进行了分析,为了增强用户身份鉴别和授权控制的安全性,分析了基 ...
- Kubernetes RBAC授权普通用户对命名空间访问权限
Kubernetes RBAC授权普通用户对命名空间访问权限 官方文档:https://www.cnblogs.com/xiangsikai/p/11413970.html kind: Role ap ...
- Chapter6_访问权限控制_类的访问权限
在Java中,访问权限修饰词也可以用于确定库中的哪些类对于该库的使用者是可用的,类既不可以是private也不可以是protected的,对于类的访问权限,只有两种选择:包访问权限或者public.下 ...
- SpringMVC之八:基于SpringMVC拦截器和注解实现controller中访问权限控制
SpringMVC的拦截器HandlerInterceptorAdapter对应提供了三个preHandle,postHandle,afterCompletion方法. preHandle在业务处理器 ...
- 基于SpringMVC拦截器和注解实现controller中访问权限控制
SpringMVC的拦截器HandlerInterceptorAdapter对应提供了三个preHandle,postHandle,afterCompletion方法. preHandle在业务处理器 ...
- SpringMVC-开启静态资源访问权限
1.配置 <mvc:resources mapping="/js/**" location="/js/"/> mapping:代表js目录下的所有文 ...
- SpringBoot整合Shiro实现基于角色的权限访问控制(RBAC)系统简单设计从零搭建
SpringBoot整合Shiro实现基于角色的权限访问控制(RBAC)系统简单设计从零搭建 技术栈 : SpringBoot + shiro + jpa + freemark ,因为篇幅原因,这里只 ...
- RBAC (基于角色的访问控制)
基于角色的访问控制(Role-Based Access Control)作为传统访问控制(自主访问,强制访问)的有前景的代替受到广泛的关注.在RBAC中,权限与角色相关联,用户通过成为适当角色的成员而 ...
随机推荐
- python的re模块一些方法 && Tkinter图形界面设计 && 终止python运行函数 && python读写文件 && python一旦给字符串赋值就不能单独改变某个字符,除非重新给变量赋值
Tkinter图形界面设计见:https://www.cnblogs.com/pywjh/p/9527828.html#radiobutton 终止python运行函数: 采用sys.exit(0)正 ...
- C#之抛异常
using System; namespace Demo { class Program { static void Main(string[] args) { try { BLLLayer(); } ...
- 要想用活Redis,Lua脚本是绕不过去的坎
前言 Redis 当中提供了许多重要的高级特性,比如发布与订阅,Lua 脚本等.Redis 当中也提供了自增的原子命令,但是假如我们需要同时执行好几个命令的同时又想让这些命令保持原子性,该怎么办呢?这 ...
- Chrony时间同步
chrony 服务器 yum -y install chrony cp /etc/chrony.conf{,.bak} #备份默认配置 cat > /etc/chrony.conf <&l ...
- Redis 穿透 & 击穿 & 雪崩
原文:https://www.cnblogs.com/binghe001/p/13661381.html 缓存穿透 如果在请求数据时,在缓存层和数据库层都没有找到符合条件的数据,也就是说,在缓存层和数 ...
- 3.keepalived+脚本实现nginx高可用
标题 : 3.keepalived+脚本实现nginx高可用 目录 : Nginx 序号 : 3 else exit 0 fi else exit 0 fi - 需要保证脚本有执行权限,可以使用chm ...
- 基于OpenCV全景拼接(Python)SIFT/SURF
一.实验内容: 利用sift算法,实现全景拼接算法,将给定的两幅图片拼接为一幅. 二.实验环境: 主机配置: CPU :intel core i5-7300 2.50GHZ RAM :8.0GB 运行 ...
- select(),fd_set(),fd_isset()
1. select函数 1. 用途 在编程的过程中,经常会遇到许多阻塞的函数,好像read和网络编程时使用的recv, recvfrom函数都是阻塞的函数,当函数不能成功执行的时候,程序就会一直阻塞在 ...
- h5py/__init__.py:36: FutureWarning: Conversion of the second argument of issubdtype from `float` to `np.floating` is deprecated
Reference 问题 ... h5py/__init__.py:36: FutureWarning: Conversion of the second argument of issubdtype ...
- zsh & for loop bug
zsh & for loop bug for: command not found syntax error near unexpected token do' do' Unicode 编码 ...