社区平台系统设计——1.1 用户子系统(权限系统)

社区平台系统设计——1.1 用户子系统(权限系统)

权限系统可以说是每个业务系统都用的到的,社区平台也不例外,由于考虑社区平台的灵活性,故现在考虑设计一个较为灵活通用的权限系统。
做了这么多年的软件,每个系统的权限管理大同小异,不过普遍不太灵活,所以每个项目也是在反复造轮子,现有公司的权限系统算是目前最灵活的,不过仍有改进余地,接下来就来讲解一下权限系统模型的演进过程。

一、权限的领域模型

1.ACL模型

ACL 是 Access Control List 的缩写,称为访问控制列表,系统给每个用户分配可操作权限的列表,当进行操作时,获取用户的权限列表判断权限是否在列表中,模型如下:

ACL领域模型类图

ACL模型比较适用于为用户分配特定资源的权限的场景,模型较为简单,容易实现对资源的权限控制,不过要求用户与资源的数量都比较少,对于大批量用户拥有类似或相同的权限的情况,管理起来比较麻烦,所以现在较多的应用于操作系统文件的权限管理。
不过我所做过的早期业务系统对权限控制不会细分到每个资源,而是一种极简的权限模型,所以会是如下的简化模型:

ACL领域模型简化版类图

这个模型相比较于ACL模型,少了资源实体,也就是说直接给用户分配权限,至于哪些资源用户可以操作,变成了业务逻辑的一部分

2.RBAC模型

RBAC是基于角色的访问控制模型,ACL模型存在着权限分配难以管理的困难,RBAC可以解决这个问题,对于一批具有相同职责的用户,会具有相同的权限,而角色就承担着这个职责。通过给用户分配角色,用户就拥有了该角色拥有的权限,每个用户可以有多个角色,每个角色可以有多个权限。模型如下图
RBAC模型类图
这个模型使得权限分配变得更为便捷,角色相当于给权限打了个包,将权限能够批量赋予用户。不过对于权限而言,权限与权限之间本身存在着一定的关联,比如一个业务的管理权限,其下可能细分了增删改查等更细粒度的权限,很多时候并不想一个个赋予权限给角色,所以后来做的业务系统中,将权限建立成了一种树状结构,模型如下:
RBAC0模型类图
不过这种以树形结构组织的权限,分配起来并不是很灵活,如果有要求将所有的读权限分配给角色,那么还是需要一个一个赋给角色。
在这里我新引入了一个权限组的概念,权限还是作为独立的个体存在,而权限组可以将权限任意的组合,原来树状结构的权限,叶子节点作为权限,枝干节点作为权限组,权限组也按树状结构组织,这样就实现了原有的模型结构,同时可以自己定义一组权限。模型如下图:
RBAC1模型类图
对于有些场景,会存在不同的场所(比如论坛,有不同的板块),每个场所可能需要维护不同的权限集,但是角色是全局概念的,所以有些系统会引入用户组的概念,但是他们都是为了将一组权限进行组合,只是适用的场所范围不同,就多一个概念感觉不太合适。如果给角色增加一个使用范围更简单些。比如版主,在自己的板块可以行使版主权限,但是在其他板块虽然是版主角色,但不能行使版主权限。
而对于有些系统,每个场所(或者可以是组织架构中的某个层级上的组织)需要独立维护自己的角色,这里完全可以将角色绑到对应的场所上。同时可以有全局角色。

3.基于规则控制的RBAC模型

之前讨论的所有模型都只是为了对用户的操作进行权限控制,但是不能对用户操作的数据进行数据范围控制,事实是存在用户只能对某个范围内的数据进行操作的需求,在过去的系统中这个职责都是有业务系统自身去实现的。但是这样就需要业务系统去管理大量的数据权限控制的查询条件。
在这里我引入用户组来作为对数据范围的控制。用户组可以关联一组数据筛选规则,也就是用户对应的角色加上用户组就确定了用户对某个实体是否能进行某个操作。模型如下:
RBAC2模型类图
图中的resourceType表示业务实体的类型,每种数据的权限都会对应筛选规则,依赖权限系统的上层应用需要获取相应的规则,将规则转化为查询条件,这样由统一的公共模块处理,业务程序中就不需要再关心数据权限的条件了。

二、权限的存储模型

1.领域模型映射为关系模型

领域模型描述了权限在客观世界中,模型之间的关系,这样便于处理业务,但是却不便于存储(目前存储还是主要基于关系模型),所以需要将领域模型映射到关系模型上。模型图如下:
这里写图片描述
图中,用户表属于外部服务,权限服务中只提供关系表的存储,所以与用户表的关联用虚线表示。过滤规则中的存储格式可以使用表达式或json存储,主要能够表示针对一个表数据如果按条件过滤。考虑到resource_type可以看作规则的一种资源类型,故可以简化模型,将resource_type作为filter_rule表的一个字段。

2.模型从性能上的优化

考虑到用户量可能会很大,所以对于用户相关的关系表进行分库分表,使用user_id作为分库分表键,其余的表使用单表设计。
既然使用了分库分表,分表之间的关联也就只有逻辑上的关联,不能进行物理关联了(sql中的join)。
从场景角度分析,目前需要的查询需求
1.根据user_id查询用户拥有的角色
2.根据user_id查询用户拥有的权限组(包含需求1的逻辑)
3.根据user_id查询用户拥有的权限(包含需求1的逻辑)
4.根据user_id查询用户拥有的用户组
5.根据user_id查询用户拥有的权限操作的规则(包含需求4的逻辑)
根据role_id或者user_group_id查user一般没有这样的场景需求,不过如果需要也可以设计一张索引表,这里就涉及到表的数据一致性问题,这个会在后面的章节中讨论这个问题。

三、性能稳定性优化

权限服务作为基础服务,会被依赖服务频繁调用。从存储模型上看,关联关系复杂,必然会导致查询较慢,所以需要给相应的数据加上缓存

1.缓存设计

缓存存储的对象可以考虑以用户维度进行缓存,将用户的角色与用户组以及相关联的信息缓存起来,这样所有根据用户维度查询用户的角色用户组等就都可以在缓存中找到了,但是由于是以用户维度作为根进行缓存的,所以用户下的权限相关数据的任意变动都会导致缓存数据变为脏数据。也就是说,无论角色修改了,还是用户组修改了,亦或是用户拥有的角色与权限组的关系发生了变化,都需要进行缓存失效。但是如果当用户量非常大时,可能导致大批量用户的权限数据失效,从而导致系统响应发生抖动。所以可以考虑用户与角色的关机以及用户与用户组的关系和用户组与角色分开缓存,查询时将缓存拼接的方式,用户与角色的变化只影响相应用户,而角色缓存失效,只会导致角色数据失效,不会将失效效应扩散开来。

2.缓存失效策略

由于用户与角色以及用户与用户组的关联数据的变化只是影响相应的用户,所以影响范围小,可以当关系表数据发生变化时,失效相应缓存。
如果角色或用户组以及他们的关联数据发生变化时,由于会有较多用户查询到失效数据,故直接失效可能会导致系统抖动,所以考虑直接在数据发生变更时,修改相应的缓存(也可以考虑异步修改缓存,看系统对实时性要求)。

3.多级缓存设计

由于之前讨论的缓存都是使用分布式缓存,在并发量很高的情况下,缓存的qps比较高,可能会扛不住,这样有导致雪崩效应的风险,所以可以在依赖的上层服务加入本地缓存,或者上层服务自己的缓存,以减缓下层服务的压力,可以考虑给外部提供的客户端sdk加入本地缓存。

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值