我们的业务系统使用了一段时间后,用户的角色类型越来越多,这时候不同类型的用户可以使用不同功能,看见不同数据的需求就变得越来越迫切。如何设计一个可扩展,且易于接入的权限系统.就显得相当重要了。结合之前我实现的的权限系统,今天就来和大家探讨一下我对权限系统的理解。
这篇文章会从权限系统业务设计,技术架构,关键代码几个方面,详细的阐述权限系统的实现。
背景
权限系统是一个系统的基础功能,但是作为创业公司,秉承着快比完美更重要原则,老系统的权限系统都是硬编码在代码或者写在到配置文件中的。随着业务的发展,如此简陋的权限系统就显得捉襟见肘了。开发一套新的,强大的权限系统就提上了日程。
这里有两个重点:
需求
权限系统需要支持功能权限和数据权限。
功能权限
所谓功能权限,就是指,拥有某种角色的用户,只能看到某些功能,并使用它。实现功能权限就简化为:
数据权限
所谓数据权限是指,数据是隔离的,用户能看到的数据,是经过控制的,用户只能看到拥有权限的某些数据。
比如,某个地区的 leader 可以查看并操作这个地区的所有员工负责的订单数据,但是员工就只能操作和查看自己负责的的订单数据。
对于数据权限,我们需要考虑的问题就抽象为角色权限表设计,
数据的归属问题:数据产生以后归属于谁?确定了数据的归属,根据某些配置,就能确定谁可以查看归属于谁的数据。业务设计
经过上面的分析,我们可以抽象出以下几个实体:
功能权限
我们知道角色权限表设计,对于一某个功能来说,它是由若干的前端元素和后端 API 组成的。
比如“合同审核” 这个功能就包括了,“查看按钮”、“审核按钮” 等前端元素。
涉及的 api 就可能包含了contract的get和patch两个 Restful 风格的接口。
抽象出来就是:在权限系统中若干前端元素和后端 API 组成了一个功能。
具体的关系,就是如下图:
permission-er
数据权限
具体每个系统的数据权限的实现有所不同贴图笔刷,我们这里实现的数据权限是依赖于公司的组织架构实现的,所有涉及到的实体如下:
这里需要说明一下,要接入数据权限,首先需要梳理数据的归属问题,数据归属于谁?或者准确的来说,数据属于哪个数据拥有者,这个数据拥有者属于哪个部门。通过这个关联关系我们就可以明确,这个数据属于哪个部门。
对于数据的使用用户游戏素材,来说,就需要查询,这个用户可以查看某个模块的某个部门的数据。
这里需要说明的是,不同的系统的数据权限需要具体分析,我们系统的数据权限是建立在公司的组织架构上的。
本质就是:
具体的关系图如下:
date-permission
注意,实际上用户和数据拥有者都是同一个实体 User 表示,只是为了表述方便进行了区分。
实现的技术难点Mysql 中树的储存
可以看出来,我们的功能和组织架构都是典型的树形结构。
我们最常见的场景如下
抽象以后就是查询树的某个节点,和他的所有子节点。
为了便于查询,我们可以增加两个冗余字段,一个是parent_id,还有一个是path。
A / B C /\ / D E F G / H I
复制
对于 D 的 path 就是(A.id).(B.id).这要的好处的就是通过sql的like的语句就能快速的查询出某个节点的子节点。
比如要获取节点 C 的所有子节点:
Select * from user where path like (A.id).(C.id).%
复制
一次查询可以获取所有子节点,是一种查询友好的设计。如果需要我们可以为path字段增加索引,根据索引的左值定律,这样的 like 查询是可以走索引的。提升查询效率。
快速的自动的获取 API 信息
我们知道Spirng mvc在启动的时候会扫描被@RequestMapping注解标记的方法,并把数据放在RequestMappingHandlerMapping中。所以我们可以这样:
@Componet public class ApiScanSerivce{ @Autoired private RequestMappingHandlerMapping requestMapping; @PostConstruct public void update(){ MaphandlerMethods = requestMapping.getHandlerMethods(); for(Map.Entry RequestMappinInfo,HandlerMethod) entry: handlerMethods.entrySet(){ // 处理 API 上传的相关逻辑 updateApiInfo(); } } }
复制