一、说明
本系统权限设计采用的是标准的RBAC模型,即基于角色的访问控制,通过用户关联角色,角色关联权限,来间接的为用户赋予权限。
用户和角色都比较熟悉了,不再赘述,这里说一下权限,在本框架中,权限分为四种:
1.1 功能菜单权限
一个系统中有很多菜单,功能菜单这个是最基本的权限,最直接的表现为哪些人登录后可以看到哪些菜单,只有看到,才有操作的可能。本系统具体体现在角色管理
->授权资源
1.2 界面按钮权限
在每一个功能菜单中,都有很多按钮,比如:增加按钮、编辑按钮、删除按钮、批量删除按钮等等,这些就是按钮权限,拥有这些按钮权限的用户,登录上来,才能看到这些按钮,才有点击的可能。本系统具体体现在角色管理
->授权资源
1.3 接口调用权限
这个权限是针对后端鉴权的,有的接口不需要登录就可以调用,有的接口登录后才能调用,有的接口需要特定角色才能调用,有的接口需要特定接口权限才能调用,这些就是接口调用的权限。之所以有接口调用权限也是为了安全,虽然从界面上已经可以通过功能菜单、界面按钮来控制哪些人能看到,哪些人能操作,但是仅仅通过前端控制显然是不安全的,稍微懂的开发技术的都可以通过api接口工具来进行接口调用,因此不得不防范。本系统具体体现在角色管理
->授权权限
1.4 数据操作权限
光能看到、能操作、能调用接口还不够,还要对这个接口可以看到哪些数据进行控制,这个就是数据操作权限。举个例子,假如有个需求,说一个公司下有10个部门,每个部门的人只能看到自己部门的数据,那么在写代码查数据的时候,可能会很简单的加上查询条件:"where my_data.org_id = 当前登录用户的orgId"
,这样确实简单,一个查询条件就可以满足需求。如果需求改为:部门经理可以看到部门数据,普通员工只可以看到自己的数据,但是技术部的张三可以看到销售部的数据,这种复杂的情况时候,显然通过改代码条件查询不那么容易实现了,这时候我们需要把这个数据权限作为“可配置”的,这就是数据权限。本系统具体体现在角色管理
->授权权限
二、菜单权限
在本框架中功能菜单权限和界面按钮权限称为资源
,接口调用权限和数据操作权限(数据范围)称为权限
。在资源授权中,功能菜单的授权是最普遍的,就是给角色授权菜单,这样拥有该角色的用户登录上来就能看到这个菜单,界面按钮的授权一般在有该特定需求的时候才会使用。另外,资源的授权和权限授权是完全独立的,资源的授权仅仅只是界面的控制,如果这些菜单调用的接口进行了接口鉴权,则需要授权菜单界面中调用的对应接口权限,否则即便看到了菜单,看到了按钮,也会提示没权限调用接口。
在本系统中,当用户登录时,系统会将用户基础信息和用户的权限码信息都缓存到redis
,并且会随着用户角色或者权限的变更而刷新,不需要重新登录。
当用户登录成功之后,会去获取用户菜单信息,这时候就会根据用户的角色去构建用户的菜单树,整个过程都是内存和redis操作,速度非常快,详见GetOwnMenu
接口
三、接口权限
菜单权限只能控制用户可以看到哪些资源,但是对于专业的人员来说,只要知道接口地址就可以跳过前端页面而是使用api调试工具来调用接口,所以我们需要对接口也加上权限控制,本系统中总共有四种接口权限,而且非常容易配置,下面就一一讲解。
3.1 所有人都能访问
没人任何限制,所有人都能访问接口,只需要知道接口地址就行,常用于登录,获取验证码等接口,只需要在接口上加上 AllowAnonymous
特性就行。
3.2 只要登录成功就能访问
默认接口权限,只需要登录成功之后携带token就能访问接口,常用于登出,获取个人信息等接口,只需在接口上加Authorize
特性,但是在注入jwt配置中我们默认是该权限,所以无需配置。
3.3 只有超级管理员可以访问
部分接口只能超级管理员才能访问,比如一些系统设置,菜单管理等,像这些接口,我们只需要在控制器或接口上加上SuperAdmin
特性,那么就代表这个控制器的所有接口都必须是管理员才能访问,如果想要某一个接口特性话只需要加上 IgnoreSuperAdmin
特性即可。
加上这个特性之后,我们在jwt授权检查中会检查接口是否又该特性,如果有那么只能超级 管理员调用否则返回403
3.4 角色需要有接口权限才能访问
限制最严格的接口权限,当前用户角色必须有该接口权限才能访问接口,比如我只想给一个用户查询权限,不给添加和删除权限,想要实现该功能,非常简单,只需在控制器加上RolePermission
特性。
当角色授权资源的时候,系统会自动扫描该菜单对应的路径也就是资源表的Path
字段,根据这个字段去扫描路由和资源表Path
一致,并且带RolePermission
特性的控制器,并将该控制器下的接口都加入到SYS_ROLE_HAS_PERMISSION
角色资源关系中,当然你也可以在部分接口加上IgnoreRolePermission
来忽略扫描。所以要注意数据库资源表path要和控制器中的route特性里面的一致
想实现该权限我们就需要在角色管理
->授权资源
中对角色有哪些资源进行配置,系统会自动将授权资源下的接口列表添加到关系表中的SYS_ROLE_HAS_PERMISSION
角色资源关系中,我们也能够从授权权限菜单中看到当前用户有哪些接口权限.
当用户请求接口,我们在jwt授权检查时,如果该路由是需要角色接口权限的,则会去检查当前用户的角色时候有权限访问,如果没有直接返回403。
四、数据权限(数据范围)
数据权限顾名思义就是用户可以查看和操作哪些数据的权限,即数据范围。举个例子,假如有个需求,说一个公司下有10个部门,每个部门的人只能看到自己部门的数据,那么在写代码查数据的时候,可能会很简单的加上查询条件:“where my_data.org_id = 当前登录用户的orgId"
,这样确实简单,一个查询条件就可以满足需求。
如果需求改为:部门经理可以看到部门数据,普通员工只可以看到自己的数据,但是技术部的张三可以看到销售部的数据,这种复杂的情况时候,显然通过改代码条件查询不那么容易实现了,这时候我们需要把这个数据权限作为“可配置”的,这就是数据权限。
明白了数据权限的定义后,我们不难发现,数据权限离不开一个重要的东西,那就是“接口”,即"在调用该接口时可以查询/操作哪些数据"
。
为什么要这么设计呢?可以想一想,如果脱离了接口,数据范围直接跟角色产生关系,即角色拥有指定的数据范围,用户拥有指定的几个角色,那么无论调用哪个接口,一个用户的数据范围都是这些,这显然不够灵活。举个例子,如果有这么个需求:部门经理可以查询全公司的数据,但是只能编辑自己部门的数据,这种需求就无法满足。
在本框架中,数据范围就是机构id集合(orgIdList),以接口进行分组,用户拥有数据范围的结果是拥有的角色在指定接口数据范围的并集,比如张三拥有A和B两个角色,针对某接口的数据范围不一致,系统将自动计算最终合并的数据范围。
4.1 数据范围类型
本系统主要有五种数据范围,可以根据不同角色灵活配置。
1.全部
:可以看到和操作所有数据,没有任何限制,可以获取所有数据范围机构Id列表。
2.仅自己
:只能看到和操作自己的数据,在代码中体现在没有任何数据范围机构。
3.所属组织
:只能看到和操作所属机构的数据,获取到的数据范围机构Id集合只有当前用户的机构Id。
4.所属组织即以下
:只能看到和操作所属机构即下级机构的数据,获取到的数据范围Id列表中有当前用户的机构id和下级机构Id。
5.自定义
:只能看到和操作指定机构的数据,当前面的数据范围无法满足时,自定义数据范围可以很好的实现多种情况。
在新建角色时,会有个默认的数据范围,当默认的数据满足不了需求时,可以在授权权限中修改你想改变数据范围接口的数据权限,这样非常的灵活。
4.2 数据范围实现
本系统实现数据范围非常简单,在服务中注入ISysUserService
,然后直接调用GetLoginUserApiDataScope
方法即可。这个数据范围拿到之后,你可以进行数据处理,你可以进行逻辑判断,你可以什么都不做,这一切完全取决于你的业务需求。
具体的实现也是很简单,因为我们已经将用户的权限缓存到了redis,所以直接从redis取就行了。
数据范围的使用在Application
模块的组织、人员、岗位三个功能中得到了淋漓尽致的体现,可参考使用。