几乎所有的系统都需要进行权限管理,可见权限管理是非常基础而重要的。比如系统包含很多模块,以模块为单位,针对不同用户进行不同操作。比如张三对组织模块只能查看,而李四既可以进行查看,也可以进行修改。所以需要对模块进行权限管理,即权限授予和验证权限两个过程。
实现思路
使用角色达到复用。角色,即权限的集合。通过授予用户角色,达到控制权限的目的。好处很明显,角色具有通用性,比如既可以授予张三管理员角色,也可以授予李四管理员角色。
也需要个性化。在享受复用的同时,也需要个性化。比如授予张三管理员的权限了,但针对张三这个人,有个操作不同于管理员,其他都一样。这时就需要个性化,继承角色的同时,也有自己的东西,就像子类继承父类,是一个道理。
抽取Model
主要负责类:用户(User),角色(Role)、资源(module)和操作许可(Permission)
衍生类:用户角色(UserRole)和用户(或者角色)对某个资源的某个操作(ACL)
Role和User多对多的关系,通过中间UsersRoles拆分成两个多对一关联。
Module自关联,用以实现模块的树形结构。
ACL(访问控制列表)的主要要素有Role、User、Module、Permission、status(允许/禁止)。用于记录用户或者角色对资源拥有的权限。
设计
问题一:多个角色授予同一用户造成权限冲突
为模块授权,通过创建角色,并为该角色分配权限,然后便可以为用户分配角色了(可以为多个),从而达到复用和统一控制的目的。
一个用户可以拥有多个角色,在给客户提供灵活方便的同时,问题来了——多个角色被授予同一个用户时出现授权冲突,比如:角色A对模块A有删除权限,但角色B对模块A的删除权限则被禁止,这时候,如果将角色A和角色B同时授予用户A,则会造成困扰,它应该是以角色A的授权为准,还是应该以角色B的授权为准?
办法总比问题多,那么我们应该考虑解决方案,并从中选择最优解。针对这个问题,可以考虑如下解决办法:
1、如果多个角色之间有授权冲突,则不允许将这些角色同时授予同一个用户,比如,在上述例子中,不允许将角色A和角色B同时授予用户A
2、允许将有授权冲突的角色同时授予同一个用户,但用户在某个时刻只能扮演其中的某个角色。在用户登陆后台管理界面之后,可以通过切换角色,来执行不同的操作!
3、允许将有授权冲突的角色同时授予同一个用户,对用户的这些角色来说,有优先级的概念,当将角色分配给用户的时候,应该设置它的优先级。同一个角色在不同的用户那里可能具有不同的优先级。当授权有冲突的时候,以优先级更高的角色授权为准。
前两种方法不够灵活,比较死板,过于强硬,而第三种方法比较人性化。
问题二:模块操作状态的存储问题
操作状态存储在ACL中,但操作至少有增删改查”四种,最直观的方式是ACL需要针对每种操作设置一个属性,来存储“允许/禁止”标识。
但是这种设计会造成灵活性的缺失。比如有可能随着需求的变更,添加了其它的操作类型,那时候必须对ACL做更改才能适应需求,更改ACL意味着更改数据库表,系,这是多么可怕的事情呀。
为了适应这种可预见的需求,可将操作及其“允许/禁止”标识设计如下:
在ACL中,设计一个int类型的状态位:intaclState,在Java中,int类型有32位,用位(bit)来表示操作类型,假设从右向左,分别代表CRUD ,位的值表示“允许/禁止”。这样,操作类型及其“允许/禁止”标识便能合二为一,而且提高了灵活性,因为最多能支持32种操作类型。
问题三:控制用户对授予权限的继承状态,实现个性化。
通过aclTriState字段来标识的办法来满足这种需求。这个额外状态位用-1(一个32位全1的int类型值),表示针对用户的授权无效,即使用继承角色的权限;用0(一个32位全0的int类型值),表示针对用户的授权有效,即使用直接授予用户的权限。
这些是对权限管理做的分析和设计,关于实现,请关注下篇博客