权限系统业务设计,技术架构,关键代码几个方面

权限系统业务设计,技术架构,关键代码几个方面

我们的业务系统使用了一段时间后,用户的角色类型越来越多,这时候不同类型的用户可以使用不同功能,看见不同数据的需求就变得越来越迫切。如何设计一个可扩展,且易于接入的权限系统.就显得相当重要了。结合之前我实现的的权限系统,今天就来和大家探讨一下我对权限系统的理解。

这篇文章会从权限系统业务设计,技术架构,关键代码几个方面,详细的阐述权限系统的实现。

背景

权限系统是一个系统的基础功能,但是作为创业公司,秉承着快比完美更重要原则,老系统的权限系统都是硬编码在代码或者写在到配置文件中的。随着业务的发展,如此简陋的权限系统就显得捉襟见肘了。开发一套新的,强大的权限系统就提上了日程。

这里有两个重点:

需求

权限系统需要支持功能权限和数据权限。

功能权限

所谓功能权限,就是指,拥有某种角色的用户,只能看到某些功能,并使用它。实现功能权限就简化为:

数据权限

所谓数据权限是指,数据是隔离的,用户能看到的数据,是经过控制的,用户只能看到拥有权限的某些数据。

比如,某个地区的 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(){
        Map handlerMethods = requestMapping.getHandlerMethods();
        for(Map.Entry RequestMappinInfo,HandlerMethod) entry: handlerMethods.entrySet(){
            // 处理 API 上传的相关逻辑
            updateApiInfo();
        }
    }
}

复制

文章来源:http://wap.hunt007.com/mip/wiki/101703.html