当前位置: 首页 > 科技观察

RBAC在TienChin项目中是如何工作的?

时间:2023-03-17 20:47:50 科技观察

本文剩下的最后一个问题是如何设置用户权限?今天我们就来聊聊这个话题。1.角色和权限首先,我们来看一下角色和权限。关于如何设计角色和权限其实有很多非常成熟的理论,最常见的就是RBAC。1.1RBAC简介RBAC(Role-basedaccesscontrol)是一种基于角色的访问控制(Role-basedaccesscontrol,RBAC)。是一种较新的、应用广泛的权限控制机制。这种机制不是直接给用户授予权限,而是给角色授予权限。RBAC权限模型按照角色对用户进行分类,通过用户的角色来判断用户是否具有对某个资源的操作权限。RBAC简化了用户和权限的管理。它将用户与角色、角色和权限以及权限与资源相关联。这种模型使得用户授权管理变得非常简单和易于维护。1.2RBAC提出的权限和角色可以在20世纪70年代初期商业计算机程序中的相关应用中找到,但是早期的程序比较简单,没有明确的、通用的、公认的权限管理模型。Ferraiolo和Kuhn在1992年提出了一种通用的基于角色的访问控制模型(这个模型好像比松哥还老),并首次提出了RBAC权限模型来替代传统的MAC和DAC。授权控制方案,并解释了RBAC中的相关概念。1992年提出的权限模型在1995年由Ferraiolo、Cugini和Kuhn进行了扩展。该模型的主要作用是所有访问都是通过角色进行的,而角色本质上是权限的集合,所有用户只能通过角色获得权限.在组织内部,角色相对稳定,而用户和权限众多且变化迅速。因此,通过角色控制权限可以简化访问控制的管理和检查。1996年,Sandhu、Coyne、Feinstein和Youman正式提出了RBAC模型,以模块化的方式对RBAC进行细化,并基于该理论提出了四种不同的模型RBAC0-RBAC3。如今,大多数信息技术供应商已将RBAC纳入其产品线。除了常规的企业级应用,RBAC还广泛应用于医疗、国防等领域。目前网上关于RBAC的理论性的东西只能找到英文的。有兴趣的朋友可以看看地址:https://csrc.nist.gov/projects/Role-Based-Access-Control各位大侠如果有中文资料的链接,请留言说明。1.3RBAC三原则最小权限:为角色配置的权限是完成任务所需的最小权限集合。职责分离:任务通过相互独立和相互排斥的角色共同完成。数据抽象:体现在权限的抽象上,RBAC支持的数据抽象程度与RBAC的实现细节有关。数据抽象:体现在权限的抽象上,RBAC支持的数据抽象程度与RBAC的实现细节有关。1.4RBAC模型分类1.4.1RBAC0RBAC0是最简单的用户、角色和权限模型。RBAC0是RBAC权限模型的核心部分,其他模型都是建立在这个基础上的。在RBAC0中,一个用户可以有多个角色,一个角色可以有多个权限,最终用户的权限是该用户拥有的角色权限的并集。1.4.2RBAC1RBAC1在RABC0的基础上引入了角色继承,允许角色之间存在上下级关系。在本系列的前几篇文章中,宋哥也多次为大家介绍了SpringSecurity中的角色继承。1.4.3RBAC2RBAC2也在RBAC0的基础上进行了扩展,引入了静态职责分离和动态职责分离。要理解职责分离,我们必须首先理解角色是相互排斥的。在实际项目中,有些角色是相互排斥、对立的。例如,财务的角色不能与其他角色结合。这个问题可以通过职责分离来解决:静态职责分离在设置阶段受到限制。例如,不能为同一个用户授予互斥的角色,一个用户只能拥有有限的几个角色,一个用户必须先有低级权限才能获得高级权限,等等。动态职责分离在运行时受到限制。例如,同一用户下的5个角色在运行时只能同时激活2个。1.4.4RBAC3将RBAC1和RBAC2组合成RBAC3。1.5扩展我们日常看到的很多权限模型都是在RBAC的基础上进行扩展的。比如在一些系统中,我们可以看到用户组的概念,就是把用户分组,用户同时有自己的角色和组角色。我们TienChin项目使用的脚手架中的权限基本都是基于RBAC权限模型。2.表的设计我们来看看若一-Vue脚手架中与用户、角色、权限相关的表。这里主要涉及到以下几个表:sys_user:这个是用户表。sys_role:这是角色表。sys_user_role:这是用户角色关联表。sys_menu:这是一个菜单表,也可以理解为资源表。sys_role_menu:这是资源角色关联表。通过用户id,可以去sys_user_role表查询该用户的角色id,然后根据角色id,去sys_role_menu表查询这个角色可以操作的资源id,然后根据resourceid,去sys_menu表中查询到对应的资源,基本上就是这么一个过程。那么如何在Java代码中做到这一点呢?3.代码实现首先定义了一个Java类SysUser,对应数据库中的sys_user表。我们看一下UserDetailsS??ervice的具体实现:@Autowired私有ISysUserService用户服务;@AutowiredprivateSysPermissionService权限服务;@OverridepublicUserDetailsloadUserByUsername(Stringusername)throwsUsernameNotFoundException{SysUseruser=userService.selectUserByUserName(用户名);if(StringUtils.isNull.log(user)){info("登录用户:{}不存在。",用户名);thrownewServiceException("登录用户:"+用户名+"不存在");}elseif(UserStatus.DELETED.getCode().equals(user.getDelFlag())){log.info("登录用户:{}已被删除。",用户名);thrownewServiceException("抱歉,您的账号:"+username+"已被删除");}elseif(UserStatus.DISABLE.getCode().equals(user.getStatus())){log.info("登录用户:{}已被停用。",用户名);thrownewServiceException("抱歉,您的账户:"+username+"已被停用");}返回createLoginUser(用户);}publicUserDetailscreateLoginUser(SysUseruser){returnnewLoginUser(user.getUserId(),user.getDeptId(),user,permissionService.getMenuPermission(user));}}queryfromdatabase我们得到的是SysUser对象,然后对该对象稍作修改,将其转化为LoginUser对象。这个LoginUser是UserDetails接口的实现类,里面存放的是当前登录用户的关键信息。在创建LoginUser对象时,有一个permissionService.getMenuPermission方法用于查询用户的权限。根据当前用户的id,查询用户的角色,再根据用户角色,查询用户的权限。此外,如果当前用户的角色是admin,则用户角色设置为*:*:*,这是硬编码的。我们再来看看LoginUser的设计:publicclassLoginUserimplementsUserDetails{/***permissionlist*/privateSetpermissions;/***用户信息*/privateSysUser用户;publicLoginUser(LonguserId,LongdeptId,SysUseruser,Setpermissions){this.userId=userId;this.deptId=deptId;这个。用户=用户;this.permissions=权限;}@JSONField(serialize=false)@OverridepublicStringgetPassword(){returnuser.getPassword();}@OverridepublicStringgetUsername(){returnuser.getUserName();/***账号是否未过期,过期无法验证*/@JSONField(serialize=false)@OverridepublicbooleanisAccountNonExpired(){returntrue;}/***指定用户是否解锁,锁定用户无法认证**@return*/@JSONField(serialize=false)@OverridepublicbooleanisAccountNonLocked(){returntrue;}/***表示用户的凭据(密码code),过期凭证阻止身份验证**@return*/@JSONField(serialize=false)@OverridepublicbooleanisCredentialsNonExpired(){returntrue;}/***可用,禁用用户无法验证**@return*/@JSONField(serialize=false)@OverridepublicbooleanisEnabled(){returntrue;}@Override公共集合getAuthorities(){返回null;下载源代码查看的朋友。可以看到这个LoginUser实现了UserDetails接口,但是和vhr有很大的区别,就是这里没有getAuthorities方法。说直接返回一个null。这里发生了什么?因为在这个脚手架中,以后在进行权限验证的时候,会这样进行:@PreAuthorize("@ss.hasPermi('system:menu:add')")@PostMappingpublicAjaxResultadd(@Validated@RequestBodySysMenumenu){//省略}@PreAuthorize注解中的@ss.hasPermi('system:menu:add')表达式表示调用Spring容器中一个名为ss的Bean的hasPermi方法来判断当前用户是否拥有名为a的权限系统:菜单:添加。名为ss的Bean的hasPermi方法如下:@Service("ss")publicclassPermissionService{/***所有权限标识*/privatestaticfinalStringALL_PERMISSION="*:*:*";/***管理用户角色权限标识符*/privatestaticfinalStringSUPER_ADMIN="admin";privatestaticfinalStringROLE_DELIMETER=",";privatestaticfinalStringPERMISSION_DELIMETER=",";/***验证用户是否有一定权限**@parampermission权限字符串*@return用户是否有一定权限*/publicbooleanhasPermi(Stringpermission){if(StringUtils.isEmpty(permission)){return错误的;}LoginUserloginUser=SecurityUtils.getLoginUser();如果(StringUtils.isNull(loginUser)||CollectionUtils.isEmpty(loginUser.getPermissions())){返回false;}返回hasPermissions(loginUser.getPermissions(),许可);}/***判断是否包含权限**@parampermissions权限列表*@parampermission权限字符串*@return用户是否拥有某个权限*/privatebooleanhasPermissions(Setpermissions,Stringpermission){returnpermissions.contains(ALL_PERMISSION)||复制代码permissions.contains(StringUtils.trim(permission));}}由于这是纯手工操作,所以比较时,直接获取当前登录用户对象LoginUser,然后手动调用其hasPermissions方法判断权限是否满足。由于都是自定义操作,UserDetails#getAuthorities方法是否实现并不重要,但是根据这里的比较方案,是不支持通配符比较的。例如,用户拥有字典表的所有操作权限,表示为system:dict:*,但与system:dict:list进行比较时,发现比较结果为false。这条思路也可以比较成功。例如,您可以使用正则表达式或其他方法进行操作。反正都是字符串比较。我相信每个人都可以自己处理。现在,前端提供了一个操作页面,还可以配置每个用户的角色,还可以配置每个角色可以操作的权限。这个比较简单,就不多说了。