基于角色访问控制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
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
附解决方案,小程序获取的用户信息中昵称图然变成了“微信用户”,而且头像也显示不了?
最近好多小伙伴在使用getUserInfo获取小程序用户昵称和头像时,图然变成了下面这样。 错误图示 很多小伙伴肯定就疑虑了,明明我代码没有做任何改动啊,怎么突然就这样了呢。还记得我们之前应该长这样啊。石头哥最近也遇到这样的问题了,我第一时间想到的原因有两个1,微信官方服务器挂了(概率很小)2,微信又改规则了。。。 带着这样的疑虑,去问了下微信小程序官方人员。得到的答案是。。。 心理一万个。。。。。 哎,没办法,既然官方说改规则,咱们作为弱势群体,只能跟着官方规则来改了啊。 代码改动 好在代码改动量不是很大,基本上改动5行代码就可以完事了。下面就来教大家如何改动代码吧。 1,先来看看老代码 js里的老代码可以看到我们用老代码,获取的就是下面这样灰色的头像和 微信用户 这样的昵称 2,认识wx.getUserProfile 其实这个wx.getUserProfile和我们之前使用button结合open-type="getUserInfo" 和bindgetuserinfo事件获取用户信息没有太大区别,所以我们先来认识下wx.getUserProfile这样改动其实还有点点好处,就是我们...
- 下一篇
【死磕JVM】给同事讲了一遍GC后,他要去面试,年轻人,就是容易冲动!
前言 在一个风和日丽的中午,和同事小勇一起走在公司楼下的小公园里面,看到很多的小姐姐,心想什么时候能够和这些小姐姐一起讨论人生呀,美滋滋,嘿嘿嘿。 收起你的哈喇子好不好,小勇总是在这个时候发出声音,挺让人喜(fu)欢(ck)的。小勇:小农,现在不是推崇垃圾分类吗,你说到底什么是垃圾?小勇总是在我和他散步的时候,问这么让人深思的问题!我:什么是垃圾啊,你不就是垃圾吗?小勇:去你大爷的,正经的。我:小勇啊,答应我以后散步的时候我们讨论点轻松点的问题好嘛?垃圾是啥,垃圾就是没有引用的对象就是垃圾啊小勇:。。。。,我们还是去午休吧 我:别啊,都讲到这里了,给你普及一下,你难道不想以后你的简历上出现——熟悉GC常用算法,熟悉常见垃圾收集器,具有实际JVM调优实战经验吗?保证让你豁然开朗,等你以后去面试的时候,给面试官讲这些保证妥妥的。 小勇:你这么说我倒是有点兴趣,但是如果讲不明白,那你就浪费了我时间了,晚饭就你请吧。我是没问题,但是我的三个粉丝不会答应你的小勇:你没问题就行了,请开始你的表演吧~ 什么是垃圾 什么是垃圾,就是没有任何引用指向的一个对象或者多个对象(循环引用),但是他们却依然占据...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- 设置Eclipse缩进为4个空格,增强代码规范
- CentOS7,8上快速安装Gitea,搭建Git服务器
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- Red5直播服务器,属于Java语言的直播服务器
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- CentOS7,CentOS8安装Elasticsearch6.8.6
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- CentOS7安装Docker,走上虚拟化容器引擎之路