最新消息:日志 随笔

Thinkphp中的RBAC权限

PHP phpthree 1254浏览

最近在用TP的RBAC权限控制,在这里记录学习一下。先来看看相关的概念

一、相关概念
访问控制与RBAC模型
1、访问控制:
        通常的多用户系统都会涉及到访问控制,所谓访问控制,是指通过某种方式允许活限制用户访问能力及范围的一种方法。这主要是由于系统需要对关键资源进行保护,防止由于非法入侵或者误操作对业务系统造成破坏。简而言之,访问控制即哪些用户可以访问哪些资源。
        一般而言,访问控制系统包括三个组成部分:
        主体:发出访问请求的实体,通常指用户或者用户进程。
        客体:被保护的资源,通常是程序,数据,文件或者设备。
        访问策略:主体和客体的映射规则,确定一个主体是否对客体具有访问能力。
2、RBAC (Role-Base-Access-Controll)
        基于角色的访问控制(RBAC)的概念在七十年代就已经提出,但是直到九十年代由于安全需求的发展才又引起了广泛关注。RBAC 的核心思想是将系统资源的访问权限进行分类或者建立层次关系,抽象为角色的概念,然后根据安全策略将用户和角色关联,从而实现了用户和权限之间的对照。RBAC 通过引入角色并将其作为权限管理的中介,将访问控制系统分为两个部分,即权限与角色的关联和角色与用户的关联,具有灵活易控制的优点。
      我的理解就是给用户赋予不同的角色,给角色服务不同的权限,权限可以看成要访问资源的集合,角色只是用户和权限的过渡层,可能会问为什要有一个角色过渡层,可以试想一下没有role过渡层,有多个用户有着相同的数个权限,分配管理权限的时候,要重复分配权限,工作量很大,更何况修改权限的时候,更麻烦,可以说不利于代码的维护,所以要有一个role的过渡层。下面来看看TP(thinkphp)中的RBAC的实现及用法。
二、TP中的RBAC类及使用RBAC类
1、RBAC权限验证的大致流程:
        ①、验证当前操作是否需要验证
        ②、验证是否登录
        ③、查看当前用户的身份
        ④、获取当前用户的权限列表
        ⑤、进行权限验证
2、RBAC所依赖的5张数据表
CREATE TABLE IF NOT EXISTS `think_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 `think_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 `think_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 `think_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;

CREATE TABLE `think_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) DEFAULT ”,
`password` varchar(25) NOT NULL,
`phone_num` varchar(20) DEFAULT NULL,
`register_time` int(11) DEFAULT NULL,
`login_time` int(11) DEFAULT NULL,
`login_ip` varchar(20) DEFAULT NULL,
`status` tinyint(1) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

数据表模型如下:
这五张表的关系如下:
一个用户对应着多个角色;一个角色可以属于对个用户;是多对多的关系,需要用个中间表即role_user表;
一个角色可以有多个权限;一个权限可以属于多个用户;是多对多的关系,需要有个中间表即access表;
那node表示什么?
node表是记录权限的表,说白了就是记录应用,控制器,及方法的表,即要访问资源的集合;比如要访问的Admin应用下的Index控制器下的index方法;从这里可以明确了TP的RBAC是控制用户对控制器及方法的访问权限进行权限的管理。下面来看个图,加深理解:
这张图里面的用户,角色,结点,权限及用户与角色中间表一一对应即可(这图是从网上盗的)。其实还可将用户进行抽象,对其进行分组,不同组别里面的角色不同,这可一根据不同的业务进行尝试。可以参考这篇博客
知道了RBAC需要的5张数据表,下面来看看RBAC类是如何实现权限验证的。
3、RBAC类
RBAC类中三个核心的方法:
    ①getAccessList()获取权限列表
static public function getAccessList($authId) {
// Db方式权限数据
$db = Db::getInstance(C(‘RBAC_DB_DSN’));
$table = array(‘role’=>C(‘RBAC_ROLE_TABLE’),’user’=>C(‘RBAC_USER_TABLE’),’access’=>C(‘RBAC_ACCESS_TABLE’),’node’=>C(‘RBAC_NODE_TABLE’));
$sql = “select node.id,node.name from “.
$table[‘role’].” as role,”.
$table[‘user’].” as user,”.
$table[‘access’].” as access ,”.
$table[‘node’].” as node “.
“where user.user_id='{$authId}’ and user.role_id=role.id and ( access.role_id=role.id or (access.role_id=role.pid and role.pid!=0 ) ) and role.status=1 and access.node_id=node.id and node.level=1 and node.status=1”;
$apps = $db->query($sql);
$access = array();
foreach($apps as $key=>$app) {
$appId = $app[‘id’];
$appName = $app[‘name’];
// 读取项目的模块权限
$access[strtoupper($appName)] = array();
$sql = “select node.id,node.name from “.
$table[‘role’].” as role,”.
$table[‘user’].” as user,”.
$table[‘access’].” as access ,”.
$table[‘node’].” as node “.
“where user.user_id='{$authId}’ and user.role_id=role.id and ( access.role_id=role.id or (access.role_id=role.pid and role.pid!=0 ) ) and role.status=1 and access.node_id=node.id and node.level=2 and node.pid={$appId} and node.status=1”;
$modules = $db->query($sql);
// 判断是否存在公共模块的权限
$publicAction = array();
foreach($modules as $key=>$module) {
$moduleId = $module[‘id’];
$moduleName = $module[‘name’];
if(‘PUBLIC’== strtoupper($moduleName)) {
$sql = “select node.id,node.name from “.
$table[‘role’].” as role,”.
$table[‘user’].” as user,”.
$table[‘access’].” as access ,”.
$table[‘node’].” as node “.
“where user.user_id='{$authId}’ and user.role_id=role.id and ( access.role_id=role.id or (access.role_id=role.pid and role.pid!=0 ) ) and role.status=1 and access.node_id=node.id and node.level=3 and node.pid={$moduleId} and node.status=1”;
$rs = $db->query($sql);
foreach ($rs as $a){
$publicAction[$a[‘name’]] = $a[‘id’];
}
unset($modules[$key]);
break;
}
}
// 依次读取模块的操作权限
foreach($modules as $key=>$module) {
$moduleId = $module[‘id’];
$moduleName = $module[‘name’];
$sql = “select node.id,node.name from “.
$table[‘role’].” as role,”.
$table[‘user’].” as user,”.
$table[‘access’].” as access ,”.
$table[‘node’].” as node “.
“where user.user_id='{$authId}’ and user.role_id=role.id and ( access.role_id=role.id or (access.role_id=role.pid and role.pid!=0 ) ) and role.status=1 and access.node_id=node.id and node.level=3 and node.pid={$moduleId} and node.status=1”;
$rs = $db->query($sql);
$action = array();
foreach ($rs as $a){
$action[$a[‘name’]] = $a[‘id’];
}
// 和公共模块的操作权限合并
$action += $publicAction;
$access[strtoupper($appName)][strtoupper($moduleName)] = array_change_key_case($action,CASE_UPPER);
}
}
return $access;
}

可以看出方法是依次获取项目模块,控制器,动作方法的权限的。

②saveAccessList()检测用户的权限列表,并将权限存储到SESSION中

 

 

原文章  http://blog.csdn.net/zp_00000/article/details/51236719

转载请注明:phph5博客 » Thinkphp中的RBAC权限