ThinkPHP中RBAC数据库详解 - 《草根》第2期
发布时间:2011-11-25 08:36:22
发布时间:2011-11-25 08:36:22
提到ThinkPHP,许多人都会为它的高效,方便所折服。但与很多学员交流后,发现大家普遍对其的RBAC权限控制感到头疼不已。那么今天就给大家带来关于这方面的教程,相信大家再看完这篇文章后,一定会对RBAC有一个清晰的了解。并且以后关于这个框架的权限控制也会信手拈来,轻松驾驭。
RBAC许多人听到后会觉得很头疼,不知道这是什么东西。用英文来讲的话就是Role-Based Access Control——基于角色的权限访问控制。呵呵,是不是听的头晕了?没那么复杂。你只需要了解一点,它是来控制用户组权限的就得了,有的用户组用户可以被添加,但是有的用户组用户却不可以,就是这样一个简单的东西。如果再搞得大家头晕一点的话,他还包括类似于安全拦截器,认证管理器...等等概念咯。我觉得如果仅仅是要使用的话,这些东西在最开始的时候其实没必要了解。只要清楚,怎么用的,以后对于这些概念即可以一看就懂,一用就通。
大家在电影当中看到过一种场景吗?——需要两个人的密码卡,指纹和眼球扫描才能够打开金库。
或者说是在现实生活中:一个营业员能进行一些基本操作,但是输入错误或者一些特殊情况下,会叫经理来刷一下卡什么的,这主要是因为营业员的权限不够。这个时候,他们的经理就会拿着自己的卡过来,然后来提升一下权限,该营业员就能够进行该项操作了。
类似于这样一种【两个身份认证】才能进行操作的功能,目前在ThinkPHP2.0的版本中,还不能实现。如果想使用这样的功能,只有大家自己写一个操作类或者是在ThinkPHP进行修改或者扩展了。
废话不多说,我们先去下载一个ThinkPHP的框架(注:在1.5版本以后ThinkPHP关于RBAC等扩展类就不包含在核心包当中了,如果大家要下载的话可以去下载最新的ThinkPHP2.0带扩展、示例和文档完整包)。下载地址如下:http://www.thinkphp.cn/Down/(别下成核心包了哦!)
下载解压完成,将其放入我们的根目录或者子目录下。
我们先跳过去这一段,讲一个权限化分中最重要,大家最容易弄混的地方,就是RBAC需要用到的表和字段。我们先来了解一下RBAC需要用到的数据表。将建立一个五张表,这五张表分别是:
1. 用户表(包含三个字段用户编号,用户名和用户密码)
2. 用户组表(用户组编号和名字)
3. 用户与组的对应关系表(组编号和用户编号)
4. 节点表(节点编号,名字,注释,父路径编号,等级)(注:这个表很容易将人搞晕)
5. 权限表(组编号,节点编号,父路径编号,等级)
以下五个表,我现在写好演示用的SQL执行语句帮助大家建表,大家在实验建表的过程中只需要复制使用下面的SQL语句即可:
表Think_access:
Role_ID | Node_id | pid | level |
用户组的编号 | 节点表 | 节点表中的父ID项 | 节点表中的等级项 |
注:如果用户组id和对应的节点id存在这张表中,就表示用户所在的用户组有权限进行对应的操作权限。
表Think_node
id | name | title | pid | level |
节点编号,用来产生关联关系,主键,自增方便索引 | 项目,模块或者动作的名字(严格区分大小写) | 项目或模块的备注,方便管理员认识和了解 | 注① | 只能为1,2,3分别代表 项目,模块,操作动作 |
注1 :
1)如果是项目名称则项目是顶级的,它上面没有父路径编号(pid),因此pid则为0;例如,网站下有一个Admin项目(分组),它是顶级的。因此没有父路径编号(pid),此处应填0,如果它为第一次插入数据库的,它的id则为1。全字段插入数据库应为:
id | name | title | pid | level |
1 | Admin | Admin项目节点 | 0 | 1 |
2)如果某项目下有一个模块。则父路径编号(pid)则为该某块所在的项目编号(id)。例如,Admin下面有一个模块为User,它是第二个插入数据库的。因此,模块的父路径(pid)应填1。全字段插入数据库应为:
id | name | title | pid | level |
2 | User | User模块节点 | 1 | 2 |
3)如果在某模块下面有三个动作(或成员方法),分别Index(显示)、insert(插入)、delete(删除),它们三个的父路径为该模块的编号。例如User模块下有index、insert、delete三个动作或成员方法,它们分别是第三,四,五次插入数据库的。他们的父路径编号(pid)应填2。全字段插入数据库应为:
id | name | title | pid | level |
3 | index | User模块下显示动作 | 2 | 3 |
4 | insert | User模块下插入动作 | 2 | 3 |
5 | delete | User模块下删除动作 | 2 | 3 |
表think_role
id | name |
组的编号(主键) | 组的名字 |
注:此表中可以插入父路径编号(pid)产生组的包含关系。对应的父路径编号(pid)为所属组的编号(id)。
表think_user
id | username | password |
用户编号(主键,int类型) | 用户名(varchar类型) | 密码(char类型32位) |
注:id在下一张表中结合think_role产生用户与组的关联关系。建议username设为唯一的,password采用32位md5加密。
表think_role_user
group_id | User_id |
组编号(主键,int类型) | 用户编号(主键,int类型) |
注:用户编号(user_id)对应哪一个group_id,则哪个用户就属于哪一个组。可以让同一个用户对应多个组。就意味着,一个用户具有多个组的属性和操作权限。
表建完了,我们现在来看一下简易图形和他们的对应关系。
最难理解,也是最核心的数据库结构和存储结构说完了,对照着这个数据库进行插入,添加修改即可。
在下期我们将接着讲解ThinkPHP的代码或数据库插入部份。希望大家很快能领悟其中,从而在开发中更加方便和快速。
李文凯 LAMP兄弟连高级讲师,资深PHP工程师,四年PHP从业经验,曾任北京华教在线技术总监。后加入北京盛远教育发展有限公司从事远程教育视频点播项目总监及网站运营经理。负责,项目的设计,开发及员工的PHP培训工作。曾收购中国自考网,北京教育网等网站。基于PHP+MySQL,分析市场和竞争对手特点后,主持开发了站群式远程视频点播平台。有丰富的B/S项目经验及运营和网站并购整合经验。
《草根》官方地址:http://www.lampbrother.net/grassroots/
上期我们讲解完了配置文件,基准文件,还有各个功能模块。这一期咱们完成thinkphpRBAC的最后一部份,写完基准类,并实现登陆以及权限的验证功能。
咱们先来说一下基准文件,基准文件的代码如下,应大家需要,我已经把代码简单的完成了,并且写完了注释,只需要复制即可。
//所有权限验证的类都需要继承这个类
class BaseAction extends Action{
//这是thinkphp中自带的一个始使化方法,加载类的时候会首先调有和这个方法
function _initialize(){
//header发送头信息,页面编码为utf=8格式
header("Content-Type:text/html; charset=utf-8");
//判断是否开启了用户认证,C()是快速读取配置文件的方法,MODEL_NAME是当前模块的名称,
//通过in_array函数检测当前调用的模块是否在不需要验证的验块中,如果是的话,取反,不进行验证。
if(C('USER_AUTH_ON') && !in_array(MODULE_NAME,explode(',',C('NOT_AUTH_MODULE')))){
//调用thinkphp自带的RBAC类
import('ORG.Util.RBAC');
//通过RBAC类中的AccessDecision获取与权限相关的信息,如果没有获取到则执行以下部份
if(!RBAC::AccessDecision()){
//获取SESSION中的用户标识号,如果没有则用户没有登陆
if(!$_SESSION[C('USER_AUTH_KEY')]){
$this->assign('jumpUrl',__APP__.'/Public/login');
$this->error('对不起,你没有登录!请重新登录');
}
//检测是否开始了游客认证方式
if(C('GUEST_AUTH_ON')){
}
//通过$this->error方法弹出错误_VALID_ACCESS_为THINKPHP已设定好的与访问//控制有关的错误提示.采用L()方式来进行读取。
$this->error(L('_VALID_ACCESS_'));
}
}
}
}
?>
基准文件说完了,那总得有一个登陆页和用户退出页吧?不然怎么激活用户的权限信息和SESSION相关的信息呢?
我们将这页命名为Public,这个模块我们仅需要做好用户登陆和用户退出两个动作即可。可继承也可以不继承BaseAction。继承的话,必须要在配置文件中加上这个模块,多个模块请用逗号分隔。'NOT_AUTH_MODULE'=>'Public',如果不加的话根本不会经过BaseActoin,也不会经过_initialize()方法。与在'NOT_AUTH_MODULE'可写上模块名称效果是一样的。切记,一定要将这个模块设为不认证的模块或不继承BaseAction,如若不然。用户进入登陆页和退出都需要相关的权限。根本登不进来,或者退不出去。
Public文件如下:、
class PublicAction extends Action {
//如果输入的是index的话也让其显示login页的模版
function index(){
$this->display('login');
}
//退出动作
public function logout()
{
//检测是否设置了USER_AUTH_KEY
if(isset($_SESSION[C('USER_AUTH_KEY')])) {
//删除USER_AUTH_KEY
unset($_SESSION[C('USER_AUTH_KEY')]);
//注稍这个session_id所有的内容
unset($_SESSION);
//调用session_destory()删除所有内容
session_destroy();
//设置退出成功的跳转页
$this->assign("jumpUrl",__URL__.'/login/');
$this->success('登陆成功!');
}else {
$this->error('已经登陆!');
}
}
// 登录检测的方法,登陆表单中的action地址需要写这一个地址
public function checkLogin() {
//如果用户名密码(可在此外加验证码)为空则直接阻止用户访问
if(empty($_POST['username'])) {
$this->error('帐号错误!');
}elseif (empty($_POST['password'])){
$this->error('密码必须!');
}
//生成认证条件
$map = array();
// 支持使用绑定帐号登录,将获得到用户名放到$map中
$map['username'] = $_POST['username'];
//加载RBAC类
import ( 'ORG.Util.RBAC' );
//通过authenticate去读取出来所有的用户信息,仅传用户名即可
$authInfo = RBAC::authenticate($map);
//使用用户名、密码和状态的方式进行认证
//如果没有获取到信息
if(false === $authInfo) {
$this->error('帐号不存在或已禁用!');
}else {
//通过$authinfo获取的信息与post当中的md5密码进行对比
if($authInfo['password'] != md5($_POST['password'])) {
$this->error('密码错误!');
}
//激活用户标识号
$_SESSION[C('USER_AUTH_KEY')] = $authInfo['id'];
//如果用户标识号是管理员,则激活管理员标识,具有一切可访问权限
if($authInfo['account']=='admin') {
$_SESSION['administrator'] = true;
}
// 通过RBAC类中的静态方法saveAccessList缓存访问权限
RBAC::saveAccessList();
$this->success('登录成功!');
}(省略了部分反括号)
整个thinkphp中RBAC的内容就全部讲解完了。如果大家有兴趣也可以到http://bbs.lampbrother.net中进行交流,整个RBAC的难点不在于代码而在于五张表之前的结构和关系有些绕,大家可以做成后台管理功能,直接通过后台对用户组和用户进行管理。有兴趣也可以在论坛中与我进行交流。我已将RBAC的实例上传到论坛中,可以通过下载实验对比杂志中的例子来提高和理解。