组织架构
1.需求点
- 查询某个组织下面的所有用户(包括所有下级组织)
- 查询某个组织下面的所有下级组织
- 查询用户所在的组织
- 获取整个组织结构,方便生成树形数据结构
2.组织表
相关的必要字段如下:
- id,主键ID
- pid,父级组织id
- tree,当前节点的所有父级组织,假设当前组织的父级id为2,2的父级id为1,则tree的值为:1,2
3.用户表
相关的必要字段如下:
- department,所在的组织id。
4.相关SQL
4.1 查询用户所在的组织
SELECT * FROM `department` `a`, INNER JOIN user `b` ON `a`.`id` = `b`.`department` WHERE b.id = "用户ID";
4.2 获取指定组织下的所有组织
SELECT * FROM `department` WHERE FIND_IN_SET( "组织ID", `tree` )
4.3 查询组织下面的所有用户
SELECT * FROM `user` `a` INNER JOIN `department` `b` ON `a`.`department` = `b`.`id` WHERE ( a.department IN (( SELECT id FROM `department` WHERE ( FIND_IN_SET( "组织ID", `tree` ) OR id = "组织ID" ) )) )
角色与权限
通常情况下有两种方式可以控制用户权限,一种是通过用户角色(Role)来控制权限,另一种是通过更细致的权限(Operation)来直接指定某个用户可以进行哪些行为。
1.角色
不同的角色可以访问的功能集合(可访问的页面)不同,创建角色时设置角色可访问的页面。
前后端分离的模式下,将角色的可访问路由传递给前端,由前端动态加载路由。
角色=可访问的页面+可操作的权限 ,按照这个进行设计,每个操作和页面关联了指定的一些接口
- RBAC0/RBAC1/RBAC2/RBAC3种,基于角色的意思是在系统中创建名为角色的桥梁,将客体的权限直接与角色进行关联,访问主体则通过被赋予的角色来访问与角色相关联的客体。
- RBAC0:最核心的模型,其主体角色、客体能够以多对多的关系进行关联:
- RBAC1:引入角色继承,将角色分为不同等级,且角色间存在上下级关系,同时支持角色间的继承
- RBAC2:在RBAC0的基础上约束控制角色,在角色被赋予客体权限及主体在获得角色时应遵循强制性约束规则,包括:互斥角色、角色被赋予主体数量约束、获得上级角色需先获得下级角色等
- RBAC3: 覆盖RBAC0/RBAC1/RBAC2所有功能。
参考:https://blog.csdn.net/weixin_42149145/article/details/112575531
2.动态路由
系统初始化的时候,只加载一些初始的路由页面(比如登录页面),登录成功后,从后台请求对应权限的路由表,然后通过router.addRoute动态添加路由。
for (let x of res) {
router.addRoute(x)
}
3.架构设计
前端可以设计如下一些功能,以Vue为例:
- 鉴权函数,auth
- 鉴权指令,v-auth
- 鉴权组件,<v-auth>
后端则按照如下规则进行权限判断:
- 角色 -> 获取可访问的页面 -> 获取页面相关的权限 -> 获取权限关联的后端接口
- 删除权限(总表)时,外键同步删除页面和权限的关联,外键又同步删除角色和权限的关联,外键同步删除权限和接口的关联
- 删除路由(总表)时,外键同步删除权限,外键同步删除页面和权限的关联,外键又同步删除角色和权限的关联,外键同步删除权限和接口的关联
- 删除角色和页面时,强制存在子元素时不可删除(逻辑上限制以及数据库外键限制)
- 父角色删除一个可访问的页面时,同步删除所有子角色的页面,同时要删除整个角色树上跟这个页面关联的所有权限
- 父角色删除一个权限时,同步删除所有角色树上子角色的权限。
- 新增页面或者权限时,同步给系统管理员新增该页面和权限。
4.问题总结
- 展示子角色的可选页面时,应该拉取它的父角色可访问页面
- 用户不可以修改自己的角色。
- 但是可以获取不包括自己角色的角色树
- 子角色新增、修改的页面,必须在父角色的页面集合内。
- 路由修改父级路由时,不能选择自己
- 删除,新增,修改角色时,都需要判断用户有没有权限
- 删除,新增,修改指定角色的页面时,都需要判断权限
- 获取权限列表,要综合父角色的已有权限和当前角色拥有的页面
- 删除角色时,判断有没有该角色的用户,有的话不允许删除
5.组织和用户
- 移动一个组织,要修改它下面所有组织的组织树
- 移动一个页面,要修改它下面所有页面的页面树
- 修改组织和页面的父级时,不能选择自身或下级组织、页面
- 上级用户修改、删除下级用户时,要先从组织和角色两方面去判断
- 删除用户时,假如用户账号内还有客户,则不允许删除。
- 删除组织时,组织内还有用户,也不允许删除
- 删除组织时,如果有子组织则不允许删除
6.权限判断
设计到权限判断的操作:新增、修改、删除、查询
判断用户对一个客户是否拥有权限
- 是不是自己的客户
- 是不是自己所在组织或下级组织的用户的客户
- 有没有处理下级用户的客户的权限
判断用户有没有操作另一个用户的权限
- 是不是自己所在组织或下级组织的用户
- 有没有处理下级组织用户的权限
判断用户有没有操作一个组织的权限
- 是不是自己所在组织或下级组织
- 有没有操作下级组织的权限
角色的上下级关系,只能用于用户添加和用户角色修改的范围限制,单纯的角色上下级关系不能作为权限判断的条件
系统分库模式
1.思考点
多公司分库模式下,多出来的一些思考点:
主表(公共表):
- 公司表
- 路由表
- 权限表
公司表:
- 角色与权限关联
- 角色与路由的关联
适配修改:
- 新增一个权限,是否是系统管理员。
- 修改角色与权限、角色与路由的关联,通过跨库外键去约束。
- 新增权限时,可选是否应用到所有公司管理员的角色上。
- 操作角色权限、组织架构判断操作的是哪个公司
2.跨公司操作
管理员跨公司操作时,需要处理的细节:
- 角色切换,如何平滑处理不同公司的角色权限的问题?
- 组织切换,如果平滑处理不同公司组织架构的问题?
- 公司切换,切换公司之后,需要判断是否具有权限以及切换对应的连接池
总结:切换公司之后,权限判断走本身所在公司的角色,部门修改为所选公司的根节点,角色修改为根角色(权限不同步改变)
- 根据中间件执行的先后顺序,接口权限判断的中间件会先于公司切换的中间件运行,在接口权限判断的阶段就缓存好用户权限,之后不再重新初始化。
问题记录
1.客户查询
查询当前用户的下级用户的所有客户的订单数据,查询出权限范围内的去重后的客户ID集合,然后再去通过in判断客户是否在这个集合内。客户数量越来越多时,如何处理?
客户的更新频率不会很高,通过建表,缓存指定用户的客户ID,并实时更新,然后将这个表用于IN查询