组织架构设计、角色与权限设计的一些思考

组织架构

1.需求点

  1. 查询某个组织下面的所有用户(包括所有下级组织)
  2. 查询某个组织下面的所有下级组织
  3. 查询用户所在的组织
  4. 获取整个组织结构,方便生成树形数据结构

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.问题总结

  1. 展示子角色的可选页面时,应该拉取它的父角色可访问页面
  2. 用户不可以修改自己的角色。
  3. 但是可以获取不包括自己角色的角色树
  4. 子角色新增、修改的页面,必须在父角色的页面集合内。
  5. 路由修改父级路由时,不能选择自己
  6. 删除,新增,修改角色时,都需要判断用户有没有权限
  7. 删除,新增,修改指定角色的页面时,都需要判断权限
  8. 获取权限列表,要综合父角色的已有权限和当前角色拥有的页面
  9. 删除角色时,判断有没有该角色的用户,有的话不允许删除

5.组织和用户

  1. 移动一个组织,要修改它下面所有组织的组织树
  2. 移动一个页面,要修改它下面所有页面的页面树
  3. 修改组织和页面的父级时,不能选择自身或下级组织、页面
  4. 上级用户修改、删除下级用户时,要先从组织和角色两方面去判断
  5. 删除用户时,假如用户账号内还有客户,则不允许删除。
  6. 删除组织时,组织内还有用户,也不允许删除
  7. 删除组织时,如果有子组织则不允许删除

6.权限判断

设计到权限判断的操作:新增、修改、删除、查询

判断用户对一个客户是否拥有权限

  • 是不是自己的客户
  • 是不是自己所在组织或下级组织的用户的客户
  • 有没有处理下级用户的客户的权限

判断用户有没有操作另一个用户的权限

  • 是不是自己所在组织或下级组织的用户
  • 有没有处理下级组织用户的权限

判断用户有没有操作一个组织的权限

  • 是不是自己所在组织或下级组织
  • 有没有操作下级组织的权限

角色的上下级关系,只能用于用户添加和用户角色修改的范围限制,单纯的角色上下级关系不能作为权限判断的条件

系统分库模式

1.思考点

多公司分库模式下,多出来的一些思考点:

主表(公共表):

  • 公司表
  • 路由表
  • 权限表

公司表:

  • 角色与权限关联
  • 角色与路由的关联

适配修改:

  • 新增一个权限,是否是系统管理员。
  • 修改角色与权限、角色与路由的关联,通过跨库外键去约束。
  • 新增权限时,可选是否应用到所有公司管理员的角色上。
  • 操作角色权限、组织架构判断操作的是哪个公司

2.跨公司操作

管理员跨公司操作时,需要处理的细节:

  • 角色切换,如何平滑处理不同公司的角色权限的问题?
  • 组织切换,如果平滑处理不同公司组织架构的问题?
  • 公司切换,切换公司之后,需要判断是否具有权限以及切换对应的连接池

总结:切换公司之后,权限判断走本身所在公司的角色,部门修改为所选公司的根节点,角色修改为根角色(权限不同步改变)

  • 根据中间件执行的先后顺序,接口权限判断的中间件会先于公司切换的中间件运行,在接口权限判断的阶段就缓存好用户权限,之后不再重新初始化。

问题记录

1.客户查询

查询当前用户的下级用户的所有客户的订单数据,查询出权限范围内的去重后的客户ID集合,然后再去通过in判断客户是否在这个集合内。客户数量越来越多时,如何处理?

客户的更新频率不会很高,通过建表,缓存指定用户的客户ID,并实时更新,然后将这个表用于IN查询。