背景

框图

上图中,Role和被设置Permission的Resource都是可以有任意层级继承关系的。

举例

举一个网站的例子来说:

如果,User表示网站用户;Role表示角色;Resource表示所有可访问的URL;Permission是对每一个URL的某一个权限(如:查看,修改等)。

Role可以有任意层级继承关系,如:用户角色可以分为Normal User和Admin User,Admin User下又可以分为Super Admin、Content Admin、Support Admin等。

URL这种Resource也可以有任意层级继承关系的,如:对http://abc.com/A/A1/A11/A111.aspx这样一个链接,可以认为http://abc.com/A1是一个URL Resource,http://abc.com/A/A1/A11是他的一个子Resource,http://abc.com/A/A1/A11/A111.aspx又是http://abc.com/A/A1/A11的一个子Resource。

对某一个Role来说,他对某一个Resource – R1的具体的Permissions,等于关联到这个Role的Resource - R1及其所有父级Resource的Permissions的并集。

对于某一个User来说,他对某一个Resource的Permissions,等于他所属的所有Roles的Permissions的并集。

问题

各元素之间的关系容易理解,关键的难点在于,因为Role和Resource都可以是有无限层级继承关系的,如何保证权限信息验证具有较高的性能呢?当继承关系较复杂时,递归检测的性能无疑是不可接受的。

数据库表

User(ID,Name)

Role(ID,Name,ParentID,LeftIndex,RightIndex

UsersInRoles(UserID,RoleID)

Resource(ID,Name,ParentID,LeftIndex,RightIndex

PermissionsOfRole(PermissionsValue,ResourceID,RoleID)

这里简单起见,对于Permissions,使用一个二进制位表示一个具体的Permission。我们需要事先定义一个PermissionsValue的每一个二进制位表示的Permission。例如:如果PermissionsValue的二进制值为10101010,表示从低位到高位第2、4、6、8位所代表的权限的并集。

使用二进制位表示一个具体的Permission的好处是,处理Permissions的并操作可以转换为二进制的OR;缺点是,具体的Permission想不能特别多,因为多一个就意味着PermissionsValue的最大值大一个2的次方。8位二进制的最大值是2的8次方,这不算很大,但是,1000位二进制的最大值是2的1000次方,这就是个不可想象的巨大数字了。好在,一般来讲,具体的Permission项目不会特别多的。

该方案的关键,就在于Role和Resource表的LeftIndex和RightIndex这两个字段了,我们将使用这两个字段,在避免递归的情况下,实现较高性能的取某个继承节点的所有子元素或所有父元素的算法。

算法

我们以Role为例,首先Role表中有且只有一条记录存放所有Roles的顶层父节点(1,“Root Role”,1,2)。当他没有子节点时,其LeftIndex和RightIndex的值分别为1和2。当对其插入子节点时,LeftIndex和RightIndex的值需要做相应的调整,调整的规则如下(括号中为LeftIndex和RightIndex的值):

按逆时针方向,大家能看出规则吗?

按照这个规则,我们可以如下获取某一个节点的所有字节点或所有父结点(使用伪SQL代码表示):

获取ID为3的Role节点的所有的子结点包括本身:

SELECT * FROM Role WHERE

LeftIndex >= (SELECT LeftIndex FROM Role WHERE ID = 3)

AND

RightIndex <= (SELECT RightIndex FROM Role WHERE ID = 3)

注:如果要不包括ID=3的节点本身,只需要用>和<代替>=和<=。

获取ID为5的Role节点的所有父节点包括本身:

SELECT * FROM Role WHERE

LeftIndex <= (SELECT LeftIndex FROM Role WHERE ID = 3)

AND

RightIndex >= (SELECT RightIndex FROM Role WHERE ID = 3)

注:如果要不包括ID=5的节点本身,只需要用>和<代替>=和<=。

大家可以根据上面的图验证一下算法的效果。完全不需要递归,只需要简单的判断LeftIndex和RightIndex就行,性能自然是非常好的。

我们甚至可以以非常简单的SQL语句获得某一个ID为2的User对ID为6的Resource的PermissionsValue:

DECLARE @PermissionsValue int;

SELECT @PermissionsValue = @PermissionsValue | PermissionsValue

FROM PermissionsOfRole WHERE

RoleID IN

(

SELECT ID FROM Role WHERE

LeftIndex >= (SELECT LeftIndex FROM Role WHERE ID IN

(SELECT RoleID FROM UsersInRoles WHERE UserID = 2))

AND

RightIndex <= (SELECT RightIndex FROM Role WHERE ID IN

(SELECT RoleID FROM UsersInRoles WHERE UserID = 2))

)

AND

ResourceID IN

(

SELECT ID FROM Resource WHERE

LeftIndex <= (SELECT LeftIndex FROM Resource WHERE ID = 6)

AND

RightIndex >= (SELECT RightIndex FROM Resource WHERE ID = 6)

);

SELECT @PermissionsValue;

上面的SQL虽然有不少嵌套的SELECT,但是,因为子查询基本上都是对主键字段的条件判断,LeftIndex和RightIndex我们也会加上索引,因此,实际上不会对性能造成太大影响。

OK,查询性能很好,不过这是以新建或修改Role和Resource的层级关系时的一定的性能损失为代价的。每次新增或修改Role或Resource的层级关系时,必须按照前面所述的规则重置所有节点的LeftIndex和RightIndex值。不过,一般情况下,由于Role和Resource的维护操作占系统整体操作的比例很小,几乎可以忽略,因此其性能损失也不是什么大问题。具体的重置所有节点LeftIndex和RightIndex值的伪代码我就不贴出来了,大家稍微花费几个脑细胞就能想出来了^-^

//结束

[转载]一种高性能Hierarchical RBAC实现方案的更多相关文章

  1. 0930MySQL中实现高性能高并发计数器方案(例如文章点击数)

    转自http://www.jb51.net/article/56656.htm 这篇文章主要介绍了MySQL中实现高性能高并发计数器方案,本文中的计数器是指如文章的点击数.喜欢数.浏览次数等,需要的朋 ...

  2. 两种高性能 I/O 设计模式 Reactor 和 Proactor

    两种高性能 I/O 设计模式 Reactor 和 Proactor Reactor 和 Proactor 是基于事件驱动,在网络编程中经常用到两种设计模式. 曾经在一个项目中用到了网络库 libeve ...

  3. I/O模型系列之四:两种高性能IO设计模式 Reactor 和 Proactor

    不同的操作系统实现的io策略可能不一样,即使是同一个操作系统也可能存在多重io策略,常见如linux上的select,poll,epoll,面对这么多不同类型的io接口,这里需要一层抽象api来完成, ...

  4. I/O模型之三:两种高性能 I/O 设计模式 Reactor 和 Proactor

    目录: <I/O模型之一:Unix的五种I/O模型> <I/O模型之二:Linux IO模式及 select.poll.epoll详解> <I/O模型之三:两种高性能 I ...

  5. 牢记负载均衡与HA,高性能是不同的方案。一般的CLUSTER只能实现其中的一种,而ORACLE的RAC可以有两种。

    F5/LVS<—Haproxy<—Squid/Varnish<—AppServer. 现在网站发展的趋势对网络负载均衡的使用是随着网站规模的提升根据不同的阶段来使用不同的技术: 第一 ...

  6. 两种高性能I/O设计模式(Reactor/Proactor)的比较

    原文出处: Alex Libman   译文出处:潘孙友   欢迎分享原创到伯乐头条 综述 这篇文章探讨并比较两种用于TCP服务器的高性能设计模式. 除了介绍现有的解决方案,还提出了一种更具伸缩性,只 ...

  7. [原创]一种Unity2D多分辨率屏幕适配方案

    此文将阐述一种简单有效的Unity2D多分辨率屏幕适配方案,该方案适用于基于原生开发的Unity2D游戏,即没有使用第三方2D插件,如Uni2D,2D toolkit等开发的游戏,NGUI插件不受这个 ...

  8. 转载 50种方法优化SQL Server数据库查询

    原文地址 http://www.cnblogs.com/zhycyq/articles/2636748.html 50种方法优化SQL Server数据库查询 查询速度慢的原因很多,常见如下几种: 1 ...

  9. <转载> 22种代码味道(Martin Fowler与Kent Beck) http://blog.csdn.net/lovelion/article/details/9301691

    Martin Fowler在Refactoring: Improving the Design of Existing Code(中译名:<重构——改善既有代码的设计>)一书中与Kent ...

随机推荐

  1. 判断IE版本的HTML语句详解,如:<!--[if IE 9]> 仅IE9可识别 <![endif]-->

    我们常常会在网页的HTML里面看到形如[if lte IE 9]……[endif]的代码,表示的是限定某些浏览器版本才能执行的语句,那么这些判断语句的规则是什么呢?请看下文: 注意:以下用法不支持IE ...

  2. Tracing SQL Queries in Real Time for MySQL Databases using WinDbg and Basic Assembler Knowledge

    https://www.codeproject.com/Articles/43305/Tracing-SQL-Queries-in-Real-Time-for-MySQL-Databas     As ...

  3. 《Windows驱动开发技术详解》之StartIO例程

    内容中包含 base64string 图片造成字符过多,拒绝显示

  4. prometheus,alertmanager 报警配置详解

    vim prometheus.yml global: scrape_interval: 15s external_labels: monitor: 'codelab-monitor' scrape_c ...

  5. nginx bind() to 0.0.0.0:**** failed (13: Permission denied)

    nginx 启动失败,日志里面报错信息如下: Starting nginx: nginx: [emerg] bind() to 0.0.0.0:**** failed (13: Permission ...

  6. cas如何去掉HTTPS认证?

    说明:默认情况下HTTP也是可以访问CAS SERVER的,但认证,登陆,退出等操作均没有任何的效果.所以必须作出下面的修改  1.进入WEB-INF\spring-configuration目录 打 ...

  7. Shell学习:read的选项及用法

    转摘: http://kb.cnblogs.com/a/2255702/ 1. Read的一些选项 Read可以带有-a, -d, -e, -n, -p, -r, -t, 和 -s八个选项. -a : ...

  8. 【Docker】mesos如何修改hostport默认端口范围?

    1.marathon文档:https://mesosphere.github.io/marathon/docs/native-docker.html Static port mapping: It's ...

  9. hdu-Coins

    http://acm.hdu.edu.cn/showproblem.php?pid=2844 Problem Description Whuacmers use coins.They have coi ...

  10. (转)一个基于vue2的天气js应用

    基于vue.js 2.0的百度天气应用 vue-weather 基于vue.js 2.0的百度天气应用. 说明 初学vue,在看完一个简单的视频教程和走两遍完官方文档之后仍然感觉云里雾里,知其然不知其 ...