RBAC全称是Role-Based Access Control,取各自的首字母。它要解决的是用户、角色、权限之间的关系,如下图所示:
它们之间,用户与角色一般是多对多的关系,角色与权限也是多对多。
ThinkPHP3.1.3中,类RBAC是在ThinkPHP/Extend/Library/ORG/Util/RBAC.class.php中,代码最上面有四张表:
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;
除了上面的四张表外,还需一张保存用户信息的用户表。在教程里,要解决的就是各用户可以具有多种角色,各角色有不同的权限。建立RbacAction.class.php,有role、node、addUser、addRole、addNode等实现角色、节点、添加用户、添加角色、添加节点的后台函数。如下:
<?php
/**
* 用户与角色关联模型
*/
class RbacAction extends CommonAction{
//用户列表
public function index(){
//D关联模型函数,实例化Model,M是普通模型,一般用来处理一个表。关联模型和视图模型一般用来处理多表操作。
//视图模型一般是处理join语句
/**
mysql> select * from think_user u left join think_role_user ru on u.id=ru.user_id left join think_role r on ru.role_id=r.id\G;
*/
$this->user=D('UserRelation')->field('password',true)->relation('role')->select();
//p($result);
//die;
$this->display();
}
//角色列表
public function role(){
$this->role=M('role')->select();
$this->display();
}
//节点列表
public function node(){
$field=array('id','name','title','pid');
$node=M('node')->field($field)->order('sort')->select();
$this->node=node_merge($node);
//p($node);
//die;
$this->display();
}
//添加用户
public function addUser(){
$this->role=M('role')->select();
$this->display();
}
//添加用户表单处理
public function addUserHandle(){
//用户信息
$user = array(
'username' => I('username'),
'password' => I('password','','md5'),
'logintime' => time(),
'loginip' => get_client_ip()
);
//所属角色
$role=array();
if($uid=M('user')->add($user)){
foreach ($_POST['role_id'] as $v) {
$role[]=array(
'role_id'=>$v,
'user_id'=>$uid
);
}
//添加用户角色
M('role_user')->add($role);
$this->success('添加用户成功',U('Admin/Rbac/index'));
}else{
$this->error('添加用户失败');
}
}
//添加角色
public function addRole(){
$this->display();
}
//添加角色表单处理
public function addRoleHandle(){
if(M('role')->add($_POST)){
$this->success('添加角色成功',U('Admin/Rbac/role'));
}else{
$this->error('添加角色失败');
}
}
//添加节点
public function addNode(){
$this->pid=I('pid',0,'intval');
$this->level=I('level',1,'intval');
switch ($this->level) {
case 1:
$this->type='应用';
break;
case 2:
$this->type='控制器';
break;
case 3:
$this->type='动作方法';
break;
}
//p($_GET);die;
$this->display();
}
//添加节点表单处理
public function addNodeHandle(){
if(M('node')->add($_POST)){
$this->success('添加节点成功',U('Admin/Rbac/node'));
}else{
$this->error('添加节点失败');
}
}
//配置权限
public function access(){
$rid=I('rid',0,'intval');
$field=array('id','name','title','pid');
$node=M('node')->order('sort')->field($field)->select();
//原有权限
$access = M('access')->where(array('role_id' => $rid))->getField('node_id',true);
$this->node=node_merge($node,$access);
$this->rid=$rid;
$this->display();
}
//修改权限
public function setAccess(){
$rid=I('rid',0,'intval');
$db=M('access');
//清空原权限
$db->where(array('role_id'=>$rid))->delete();
//组全新权限
$data=array();
foreach ($_POST['access'] as $v) {
$tmp = explode('_',$v);
$data[]=array(
'role_id' => $rid,
'node_id' => $tmp[0],
'level' => $tmp[1]
);
}
//插入新权限
if($db->addAll($data)){
$this->success('修改权限成功',U('Admin/Rbac/role'));
}else{
$this->error('修改权限失败');
}
//p($data);
}
}
?>
另外还需要建立用户与角色的模型,在Model文件夹下建立UserRelationModel.class.php,代码如下:
<?php
/**
* 用户与角色关联模型
*/
class UserRelationModel extends RelationModel{
//定义主表名称
protected $tableName = 'user'; //此变量默认为类名去掉Model,即UserRelation,但我的关联表是user,因此要配置一下
//定义关联关系
protected $_link=array(
'role'=>array(
'mapping_type'=>MANY_TO_MANY, //HAS_ONE是一对一,HAS_MANY是一对多,MANY_TO_MANY是多对多
'foreign_key'=>'user_id', //主表外键名称。主表在中间表中的字段名称
'relation_key'=>'role_id', //副表外键名称,副表在中间表中的字段名称
'relation_table'=>'think_role_user', //中间表名称
'mapping_fields'=>'id,name,remark' //副表要读取的项
)
);
}
?>
当然前台的Tpl下还要建立Rbac文件夹,建立role.html、node.html、addUser.html等对应后台的函数。
还有Conf/config.php里面要配置,可以配置的有:
<?php
/**
* Description: ThinkPHP中RBAC处理类的配置文件
*/
return array(
"USER_AUTH_ON" => true, //是否开启权限验证(必配)
"USER_AUTH_TYPE" => 1, //验证方式(1、登录验证;2、实时验证)
"USER_AUTH_KEY" => 'uid', //用户认证识别号(必配)
"ADMIN_AUTH_KEY" => 'superadmin', //超级管理员识别号(必配)
"USER_AUTH_MODEL" => 'user', //验证用户表模型 ly_user
'USER_AUTH_GATEWAY' => '/Public/login', //用户认证失败,跳转URL
'AUTH_PWD_ENCODER'=>'md5', //默认密码加密方式
"RBAC_SUPERADMIN" => 'admin', //超级管理员名称
"NOT_AUTH_MODULE" => 'Index', //无需认证的控制器
"NOT_AUTH_ACTION" => 'addRoleHandle,addUserHandle,addNodeHandle,setAccess', //无需认证的方法
'REQUIRE_AUTH_MODULE' => '', //默认需要认证的模块
'REQUIRE_AUTH_ACTION' => '', //默认需要认证的动作
'GUEST_AUTH_ON' => false, //是否开启游客授权访问
'GUEST_AUTH_ID' => 0, //游客标记
"RBAC_ROLE_TABLE" => 'think_role', //角色表名称(必配)
"RBAC_USER_TABLE" => 'think_role_user', //用户角色中间表名称(必配)
"RBAC_ACCESS_TABLE" => 'think_access', //权限表名称(必配)
"RBAC_NODE_TABLE" => 'think_node', //节点表名称(必配)
);
总的来说其实现的是:
用户(增、删、改、查) 角色(增、删、改、查) 节点(增、删、改、查) 配置权限(更新权限)
网上查得,RBAC还提供下面这些静态函数:
方法名 | 接收参数说明 | 返回值 | 说明 |
---|---|---|---|
RBAC::authenticate($map,$model=''); |
|
array |
返回值是在用户表中,以$map为条件 |
0RBAC::saveAccessList($authId=null); |
|
返回一个空值 | 如果验证方式为登录验证,则将权限写入session中,否则不作任何处理 |
RBAC::getRecordAccessList($authId=null,$module=''); |
|
Array | 返回一个包含权限的ID的数组 |
RBAC::checkAccess() | 无 | 返回true或false | 检查当前操作是否需要认证(根据配置中需要认证和不需要评论的模块或方法得出) |
RBAC::checkLogin() | 无 | true | 如果当前操作需要认证且用户没有登录,继续检测是否开启游客授权。如果开启游客授权,则写入游客权限;否则跳到登录页 |
RBAC::AccessDecision($appName=APP_NAME) |
|
|
AccessDecision($appName=APP_NAME)方法,检测当前项目模块操作,是否存在于$_SESSION['_ACCESS_LIST']数组中$_SESSION['_ACCESS_LIST']['当前操作']['当前模块']['当前操作']是否存在。如果存在表示有权限,返回true;否则返回flase。 |
RBAC::getAccessList($authId) |
|
Array | 通过数据库查询取得当前认证号的所有权限列表 |
RBAC::getModuleAccessList($authId,$module) |
|
Array | 返回指定用户可访问的节点权限数组 |
路过,留个脚印吧
@火锅桌椅 欢迎光临~