RBAC是什么,能解决什么难题?
RBAC是Role-Based Access Control的首字母,译成中文即基于角色的权限访问控制,说白了也就是用户通过角色与权限进行关联[其架构灵感来源于操作系统的GBAC(GROUP-Based Access Control)的权限管理控制]。简单的来说,一个用户可以拥有若干角色,每一个角色拥有若干权限。这样,就构造成“用户-角色-权限”的授权模型。在这种模型中,用户与角色之间,角色与权限之间,一般者是多对多的关系。其对应关系如下:
在许多的实际应用中,系统不只是需要用户完成简单的注册,还需要对不同级别的用户对不同资源的访问具有不同的操作权限。且在企业开发中,权限管理系统也成了重复开发效率最高的一个模块之一。而在多套系统中,对应的权限管理只能满足自身系统的管理需要,无论是在数据库设计、权限访问和权限管理机制方式上都可能不同,这种不致性也就存在如下的憋端:
- 维护多套系统,重复造轮子,时间没用在刀刃上
- 用户管理、组织机制等数据重复维护,数据的完整性、一致性很难得到保障
- 权限系统设计不同,概念理解不同,及相应技术差异,系统之间集成存在问题,单点登录难度大,也复杂的企业系统带来困难
RBAC是基于不断实践之后,提出的一个比较成熟的访问控制方案。实践表明,采用基于RBAC模型的权限管理系统具有以下优势:由于角色、权限之间的变化比角色、用户关系之间的变化相对要慢得多,减小了授权管理的复杂性,降低管理开销;而且能够灵活地支持应用系统的安全策略,并对应用系统的变化有很大的伸缩性;在操作上,权限分配直观、容易理解,便于使用;分级权限适合分层的用户级形式;重用性强。
ThinkPHP中RBAC实现体系
ThinkPHP中RBAC基于Java的Spring的Acegi安全系统作为参考原型,并做了相应的简化处理,以适应当前的ThinkPHP结构,提供一个多层、可定制的安全体系来为应用开发提供安全控制。安全体系中主要有以下几部分:
- 安全拦截器
- 认证管理器
- 决策访问管理器
- 运行身份管理器
安全拦截器
安全拦截器就好比一道道门,在系统的安全防护系统中可能存在很多不同的安全控制环节,一旦某个环节你未通过安全体系认证,那么安全拦截器就会实施拦截。
认证管理器
防护体系的第一道门就是认证管理器,认证管理器负责决定你是谁,一般它通过验证你的主体(通常是一个用户名)和你的凭证(通常是一个密码),或者更多的资料来做到。更简单的说,认证管理器验证你的身份是否在安全防护体系授权范围之内。
访问决策管理
虽然通过了认证管理器的身份验证,但是并不代表你可以在系统里面肆意妄为,因为你还需要通过访问决策管理这道门。访问决策管理器对用户进行授权,通过考虑你的身份认证信息和与受保护资源关联的安全属性决定是是否可以进入系统的某个模块,和进行某项操作。例如,安全规则规定只有主管才允许访问某个模块,而你并没有被授予主管权限,那么安全拦截器会拦截你的访问操作。
决策访问管理器不能单独运行,必须首先依赖认证管理器进行身份确认,因此,在加载访问决策过滤器的时候已经包含了认证管理器和决策访问管理器。
为了满足应用的不同需要,ThinkPHP 在进行访问决策管理的时候采用两种模式:登录模式和即时模式。登录模式,系统在用户登录的时候读取改用户所具备的授权信息到 Session,下次不再重新获取授权信息。也就是说即使管理员对该用户进行了权限修改,用户也必须在下次登录后才能生效。即时模式就是为了解决上面的问题,在每次访问系统的模块或者操作时候,进行即使验证该用户是否具有该模块和操作的授权,从更高程度上保障了系统的安全。
运行身份管理器
运行身份管理器的用处在大多数应用系统中是有限的,例如某个操作和模块需要多个身份的安全需求,运行身份管理器可以用另一个身份替换你目前的身份,从而允许你访问应用系统内部更深处的受保护对象。这一层安全体系目前的 RBAC 中尚未实现。
ThinkPHP中RBAC认证流程
对应上面的安全体系,ThinkPHP 的 RBAC 认证的过程大致如下:
- 判断当前模块的当前操作是否需要认证
- 如果需要认证并且尚未登录,跳到认证网关,如果已经登录 执行5
- 通过委托认证进行用户身份认证
- 获取用户的决策访问列表
- 判断当前用户是否具有访问权限
权限管理的具体实现过程
RBAC相关的数据库介绍
在ThinkPHP完整包,包含了RBAC处理类RBAC.class.php文件,
位于Extend/Library/ORG/Util
。打开该文件,其中就包含了使用RBAC必备的4张表,SQL语句如下(复制后请替换表前缀):
- CREATE TABLE IF NOT EXISTS `ly_access` (
- `role_id` smallint(6) unsigned NOT NULL,
- `node_id` smallint(6) unsigned NOT NULL,
- `level` tinyint(1) NOT NULL,
- `module` varchar(50) DEFAULT NULL,
- KEY `groupId` (`role_id`),
- KEY `nodeId` (`node_id`)
- ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
- CREATE TABLE IF NOT EXISTS `ly_node` (
- `id` smallint(6) unsigned NOT NULL AUTO_INCREMENT,
- `name` varchar(20) NOT NULL,
- `title` varchar(50) DEFAULT NULL,
- `status` tinyint(1) DEFAULT '0',
- `remark` varchar(255) DEFAULT NULL,
- `sort` smallint(6) unsigned DEFAULT NULL,
- `pid` smallint(6) unsigned NOT NULL,
- `level` tinyint(1) unsigned NOT NULL,
- PRIMARY KEY (`id`),
- KEY `level` (`level`),
- KEY `pid` (`pid`),
- KEY `status` (`status`),
- KEY `name` (`name`)
- ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
- CREATE TABLE IF NOT EXISTS `ly_role` (
- `id` smallint(6) unsigned NOT NULL AUTO_INCREMENT,
- `name` varchar(20) NOT NULL,
- `pid` smallint(6) DEFAULT NULL,
- `status` tinyint(1) unsigned DEFAULT NULL,
- `remark` varchar(255) DEFAULT NULL,
- PRIMARY KEY (`id`),
- KEY `pid` (`pid`),
- KEY `status` (`status`)
- ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ;
- CREATE TABLE IF NOT EXISTS `ly_role_user` (
- `role_id` mediumint(9) unsigned DEFAULT NULL,
- `user_id` char(32) DEFAULT NULL,
- KEY `group_id` (`role_id`),
- KEY `user_id` (`user_id`)
- ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
下面对RBAC相关的数据库表及字段作一下介绍:
表名 | 字段名 | 字段类型 | 作用 |
---|---|---|---|
ly_user | id | INT | 用户ID(唯一识别号) |
username | VARCHAR(16) | 用户名 | |
password | VARCHAR(32) | 密码 | |
VARCHAR(100) | 用户邮箱 | ||
create_time | TIMESTAMP | 创建时间(时间戳) | |
logintime | TIMESTAMP | 最近一次登录时间(时间戳) | |
loginip | VARCHAR(15) | 最近登录的IP地址 | |
status | TINYINT(1) | 启用状态:0:表示禁用;1:表示启用 | |
remark | VARCHAR(255) | 备注信息 | |
ly_role | id | INT | 角色ID |
name | VARCHAR(20) | 角色名称 | |
pid | SMALLINT(6) | 父角色对应ID | |
status | TINYINT(1) | 启用状态(同上) | |
remark | VARCHAR(255) | 备注信息 | |
ly_node | id | SMALLINT(6) | 节点ID |
name | VARCHAR(20) | 节点名称(英文名,对应应用控制器、应用、方法名) | |
title | VARCHAR(50) | 节点中文名(方便看懂) | |
status | TINYINT(1) | 启用状态(同上) | |
remark | VARCHAR(255) | 备注信息 | |
sort | SMALLINT(6) | 排序值(默认值为50) | |
pid | SMALLINT(6) | 父节点ID(如:方法pid对应相应的控制器) | |
level | TINYINT(1) | 节点类型:1:表示应用(模块);2:表示控制器;3:表示方法 | |
ly_role_user | user_id | INT | 用户ID |
role_id | SMALLINT(6) | 角色ID | |
ly_access | role_id | SMALLINT(6) | 角色ID |
node_id | SMALLINT(6) | 节点ID | |
level | |||
module |
以下是数据库表各字段的关联关系:
实现RBAC管理的前导性工作
基于ThinkPHP实现RBAC的权限管理系统中,首先要做一些前导性的工作(系统数据库设计TP已经为我们完成了),主要分以下几个方面:
- 用户(增、删、改、查)
- 角色(增、删、改、查)
- 节点(增、删、改、查)
- 配置权限(更新权限)
具体实现的代码如下(相关解释均在注释之中):
- <?php
- /**
- * Created by PhpStorm.
- * User: LiuYang
- * Date: 14-9-6
- * Time: 下午9:54
- * Description: 基于ThinkPHP实现的权限管理系统
- */
- class RbacAction extends CommonAction {
- //初始化操作
- function _initialize(){
- if(!IS_AJAX) $this->error('你访问的页面不存在,请稍后再试');
- }
- //用户列表
- public function index(){
- $db = M('user');
- //当前页码
- $pageNum = I('post.pageNum',1,'int');
- //每页显示条数
- $numPerPage = I('post.numPerPage',C("numPerPage"),'int');
- //总页码数
- $totalCount = $db->count();
- $this->totalCount = $totalCount;
- $this->numPerPage = $numPerPage;
- $this->items = D('UserRelation')->relation(true)->page($pageNum, $numPerPage)->select();
- $this->display();
- }
- //添加编辑用户弹层表单
- public function addUser(){
- //如果设置了uid,则为编辑用户,否则为增加用户
- $this->role = M('role')->where('status = 1')->field('id,name')->select();