妙用sql的统计进行集合的比较
在阳光网改版过程中,引入的权限控制是个让人喜爱又头疼的问题。
假如所有的权限或者资源数据是静态的化,我会非常喜欢,因为通过简单的代码和控制策略就可以提升系统的安全性。
但,这是不可能的。在权限系统中,所有的资源应该能够动态的添加和增加。尤其在未来的某段时间内,阳光网的所有资源进行整合的时候,添加增减资源将会时常的发生。更为重要的,让系统能够容纳更多的用户进行系统内部的控制,一个必然的需求,就是让系统更灵活,更好的实现不同场景下,资源的分配和组织。
所有的问题都来自于动态的组织上,下面是其中的一方面。
在权限模块中,我们采用的3张表(此次问题涉及的3站,在实际控制中,权限表较多),role,module,operation。分别对应角色,模块和操作,模块用来组织操作,便于操作的分配。
在控制环境中,这些表的关系再复杂都不会影响权限的控制,但在存在多层分类,如角色,子角色;模块,子模块,的时候,界面元素的分配将成为一个问题。
默认情况下一个模块绑定一个界面元素。这是最完美的情况。对于代码而言,就是获取角色关联的模块,然后呈现与资源进行绑定的模块,一气呵成。
此情况在开始开发系统的时候是比较常见的,但在随后的过程中,一个模块可能不再与一个界面资源绑定;或者一个角色所关联的模块自身没有界面资源,但其操作进行重组后的模块确是绑定界面资源的,这个比较复杂。
举例
角色a->模块a->操作1操作2操作3
角色b->(模块b->操作1操作2),(模块c>操作3)
其中模块b,c与界面资源进行了合理的绑定(为了呈现而采用的一种web动态布局方式);而界面模块a没有。对于分配到角色b的用户来说,没有任何界面资源的控制问题;倘若用户a同时拥有角色a,b,那么很幸运,通过角色查询到的模块b,c就可以呈现出界面资源;但用户b只具有模块a,从权限上考虑,用户b具有进行操作1操作2操作3的使用权,这在桌面系统中(无图形桌面,只是用控制命令),这是完全合理的。但WEB就不行。
唯一的解决策略就是反向查找。
具体通过角色a查找到所有有效操作,再将操作反向到所有可能的模块。
思路很简单,但编程却不容易。在我们的系统中,角色模块是动态的,一个用户的多个角色需要进行合并,多个角色关联的多个模块同样需要合并同时去除重复项。实际过程中是够复杂的。
但若站在数据库层次考虑(这是我比较赞同的思考方向,对于基于数据库的应用,数据的控制问题尽量不干预业务),需要以下形式的sql语句经行查询就可以获取需要的数据。
SELECT DISTINCT temp1.ModuleID
FROM
(
SELECT oinm1.ModuleID,COUNT(DISTINCT oinm1.OperationID) AS opsum
FROM OperationInModule AS oinm1, OperationInModule AS oinm2
WHERE oinm1.OperationID = oinm2.OperationID AND oinm2.ModuleID
IN
(
SELECT DISTINCT rbac_minr.ModuleID
FROM ModuleInRole AS rbac_minr, UserInRoles as rbac_uinr
WHERE rbac_minr.RoleID = rbac_uinr.RoleID AND rbac_uinr.UserID = @UserID
)
GROUP BY oinm1.ModuleID
) AS temp1,
(
SELECT ModuleID,COUNT(*) AS opsum
FROM OperationInModule GROUP BY ModuleID
) AS temp2
WHERE temp1.ModuleID = temp2.ModuleID AND temp1.opsum = temp2.opsum
虽然省略的关系表,但通过sql语句还是很明显理解其中的表关系的。在这个查询过程中,通过sql的统计就可以轻松的实现集合的比较。由此就可以有效地解决模块的方向查找的问题。
界面和模块的分配由此可以完成,可以说算得上圆满。但另有一个问题又浮出水面。如果模块a也绑定了界面资源,但和模块b的界面资源又不一样。对于一个同时拥有角色ab的用户来说,上面的方向查询能够获得模块a,b,具体呈现哪一种界面资源;还有一些情况更复杂,在此就不说了。