参考借鉴 shiro和spring security的权限管理, 100% 测试覆盖
访问控制列表。
权限判断辅助规则, 目前需要硬编码。
在有些时候,ACL判断规则会比较繁琐,例如:
在分级权限管理中,假如分为四级:总部级,省级,市级,县级。 省级管理人员可以管理下面所有市级、县级的营业厅,市级管理人员可以管理所有的本市的营业厅和下面县的营业厅,A市共有100家营业厅。
如果按照ACL的规则,则需要给省级管理人员配置所有营业厅的管理权限,就是需要N条记录。而且,每增加、删除营业厅,都需要重新修改权限 的ACL规则。
按照rule的配置规则,则仅仅需要编写一个函数,判断省级的管理人员是否有该营业厅的权限即可。
当然,rule函数规则和业务关联紧密,需要在代码中写死,灵活性欠缺。
一个接口,特指Account或者Role
你需要自己定义Account Model,并为Account实现 Principal 接口的两个方法:
type AclObject interface {
GetSid() string // 通常是 uuid
GetTyp() string // object类型, user, role, post, etc...
}
type Principal interface {
AclObject
}
Account 的 GetSid方法返回Account的Sid,通常为UUID Account 的 GetTyp方法返回 acl.AccountTyp, 为"AccountTyp"
Account 要操作的权限标的必须实现 AclObject 接口.
权限标的 GetTyp 方法返回权限标的 类型。
角色最重要的两个接口:
- 创建角色
func (mgr *AclManager) CreateRole(name, sid, depart, corp, city, province string, level int) (*Role, error) {
- name: 角色名称
- sid: 角色全局id, 通常为空, 由uuid自动生成
- depart: 角色所在部门
- corp: 角色所在公司
- city: 角色所在城市
- province: 角色所在省份
- level: 角色级别
- 将Account加入到角色中:
func (mgr *AclManager) AddUserRoleRelation(uid, rid string) error
- uid: Account的uuid
- rid: role的uuid
Permission 比较简单, 核心是一个类型为uint32, 名为Mask的字段。 通过 请求的操作权限 与 AclEntry 中的权限做 & 操作, 来决定是否具备操作权限
Mask每一位代表一种权限,目前已定义的权限如下:
PermissionRead = 1 << permIndexRead // 读权限
PermissionWrite = 1 << permIndexWrite // 写权限
PermissionCreate = 1 << permIndexCreate // 创建权限
PermissionDelete = 1 << permIndexDelete // 删除权限
PermissionManage = 1 << permIndexManage // 管理权限
通过AclEntry来控制一个账号或一个角色是否对权限标的有特定的权限
创建ACL entry:
func (mgr *AclManager) CreateACE(whoSid, whoTyp, whatSid, whatTyp string, mask uint32, order int,
grant, auditSuccess, auditFailure bool) (*AclEntry, error)
- whoSid: 权限主体sid
- whoTyp: 权限主体类型, 通常为AccountTyp或RoleTyp
- whatSid: 权限标的sid
- whatTyp: 权限标的类型
- mask: 权限
- order: 创建的Acl在Acl list的顺序, 判断权限时, AclEntry安装order从小到大逐条判定
- grant: 授权或拒绝
- auditSuccess: 当授权时, 是否审计
- auditFailure: 当拒绝时, 是否审计
func CreateAclManager(db *gorm.DB, pool *redis.Pool) *AclManager
CreateAclManager 用来创建 AclManager 对象
在上面的工作都准备好以后,就可以通过IsGrant接口来查询权限:
func (mgr *AclManager) IsGrant(who AclObject, what AclObject, perm Permission) bool {
- who: 权限主体, 可以是Account,也可以是Role
- what: 权限标的
- perm: 请求的权限
- 返回: true: 授权 false:拒绝
权限判断通过这个接口来查询:
func (mgr *AclManager) IsGrant(who AclObject, what AclObject, perm Permission) bool
流程:
-
首先,根据who查找who的principls, 即who本身和who所属的roles;
-
对于who所属的每个principal:
a. 查找who关联的rules, 轮询每条rule, 该rule 是否能够判定权限, 如果该rule判定出权限(Grant或Reject), 返回权限; 否则, 返回0;
b. 查找who关联所有 acl, 轮询每条acl, 该acl 是否能够判定权限, 如果能判定出权限(Grant或Reject), 返回权限; 否则, 返回0;
-
如果以上步骤未判定出权限, 返回拒绝(Reject)
查询权限时,优先从缓存中查找。
缓存使用redis保存,主要有以下2种:
- user对应的角色列表, 使用 redis set 保存;
- principal 对应的 acl 列表, 使用 redis hashmap 保存;