ThinkPHP权限认证Auth实例详解
本文以实例代码的形式深入剖析了ThinkPHP权限认证Auth的实现原理与方法,具体步骤如下:
mysql数据库部分sql代码:
------------------------------
--Tablestructureforthink_auth_group
------------------------------
DROPTABLEIFEXISTS`think_auth_group`;
CREATETABLE`think_auth_group`(
`id`mediumint(8)unsignedNOTNULLAUTO_INCREMENT,
`title`char(100)NOTNULLDEFAULT'',
`status`tinyint(1)NOTNULLDEFAULT'1',
`rules`char(80)NOTNULLDEFAULT'',
PRIMARYKEY(`id`)
)ENGINE=MyISAMAUTO_INCREMENT=2DEFAULTCHARSET=utf8COMMENT='用户组表';
------------------------------
--Recordsofthink_auth_group
------------------------------
INSERTINTO`think_auth_group`VALUES('1','管理组','1','1,2');
------------------------------
--Tablestructureforthink_auth_group_access
------------------------------
DROPTABLEIFEXISTS`think_auth_group_access`;
CREATETABLE`think_auth_group_access`(
`uid`mediumint(8)unsignedNOTNULLCOMMENT'用户id',
`group_id`mediumint(8)unsignedNOTNULLCOMMENT'用户组id',
UNIQUEKEY`uid_group_id`(`uid`,`group_id`),
KEY`uid`(`uid`),
KEY`group_id`(`group_id`)
)ENGINE=MyISAMDEFAULTCHARSET=utf8COMMENT='用户组明细表';
------------------------------
--Recordsofthink_auth_group_access
------------------------------
INSERTINTO`think_auth_group_access`VALUES('1','1');
INSERTINTO`think_auth_group_access`VALUES('1','2');
------------------------------
--Tablestructureforthink_auth_rule
------------------------------
DROPTABLEIFEXISTS`think_auth_rule`;
CREATETABLE`think_auth_rule`(
`id`mediumint(8)unsignedNOTNULLAUTO_INCREMENT,
`name`char(80)NOTNULLDEFAULT''COMMENT'规则唯一标识',
`title`char(20)NOTNULLDEFAULT''COMMENT'规则中文名称',
`status`tinyint(1)NOTNULLDEFAULT'1'COMMENT'状态:为1正常,为0禁用',
`type`char(80)NOTNULL,
`condition`char(100)NOTNULLDEFAULT''COMMENT'规则表达式,为空表示存在就验证,不为空表示按照条件验证',
PRIMARYKEY(`id`),
UNIQUEKEY`name`(`name`)
)ENGINE=MyISAMAUTO_INCREMENT=5DEFAULTCHARSET=utf8COMMENT='规则表';
------------------------------
--Recordsofthink_auth_rule
------------------------------
INSERTINTO`think_auth_rule`VALUES('1','Home/index','列表','1','Home','');
INSERTINTO`think_auth_rule`VALUES('2','Home/add','添加','1','Home','');
INSERTINTO`think_auth_rule`VALUES('3','Home/edit','编辑','1','Home','');
INSERTINTO`think_auth_rule`VALUES('4','Home/delete','删除','1','Home','');
DROPTABLEIFEXISTS`think_user`;
CREATETABLE`think_user`(
`id`int(11)NOTNULL,
`username`varchar(30)DEFAULTNULL,
`password`varchar(32)DEFAULTNULL,
`age`tinyint(2)DEFAULTNULL,
PRIMARYKEY(`id`)
)ENGINE=InnoDBDEFAULTCHARSET=utf8;
------------------------------
--Recordsofthink_user
------------------------------
INSERTINTO`think_user`VALUES('1','admin','21232f297a57a5a743894a0e4a801fc3','25');
配置文件Application\Common\Conf\config.php部分:
<?php returnarray( //'配置项'=>'配置值' 'DB_DSN'=>'',//数据库连接DSN用于PDO方式 'DB_TYPE'=>'mysql',//数据库类型 'DB_HOST'=>'localhost',//服务器地址 'DB_NAME'=>'thinkphp',//数据库名 'DB_USER'=>'root',//用户名 'DB_PWD'=>'root',//密码 'DB_PORT'=>3306,//端口 'DB_PREFIX'=>'think_',//数据库表前缀 'AUTH_CONFIG'=>array( 'AUTH_ON'=>true,//认证开关 'AUTH_TYPE'=>1,//认证方式,1为时时认证;2为登录认证。 'AUTH_GROUP'=>'think_auth_group',//用户组数据表名 'AUTH_GROUP_ACCESS'=>'think_auth_group_access',//用户组明细表 'AUTH_RULE'=>'think_auth_rule',//权限规则表 'AUTH_USER'=>'think_user'//用户信息表 ) );
项目Home控制器部分Application\Home\Controller\IndexController.class.php代码:
<?php
namespaceHome\Controller;
useThink\Controller;
classIndexControllerextendsController{
publicfunctionindex(){
$Auth=new\Think\Auth();
//需要验证的规则列表,支持逗号分隔的权限规则或索引数组
$name=MODULE_NAME.'/'.ACTION_NAME;
//当前用户id
$uid='1';
//分类
$type=MODULE_NAME;
//执行check的模式
$mode='url';
//'or'表示满足任一条规则即通过验证;
//'and'则表示需满足所有规则才能通过验证
$relation='and';
if($Auth->check($name,$uid,$type,$mode,$relation)){
die('认证:成功');
}else{
die('认证:失败');
}
}
}
以上这些代码就是最基本的验证代码示例。
下面是源码阅读:
1、权限检验类初始化配置信息:
$Auth=new\Think\Auth();
创建一个对象时程序会合并配置信息
程序会合并Application\Common\Conf\config.php中的AUTH_CONFIG数组
publicfunction__construct(){
$prefix=C('DB_PREFIX');
$this->_config['AUTH_GROUP']=$prefix.$this->_config['AUTH_GROUP'];
$this->_config['AUTH_RULE']=$prefix.$this->_config['AUTH_RULE'];
$this->_config['AUTH_USER']=$prefix.$this->_config['AUTH_USER'];
$this->_config['AUTH_GROUP_ACCESS']=$prefix.$this->_config['AUTH_GROUP_ACCESS'];
if(C('AUTH_CONFIG')){
//可设置配置项AUTH_CONFIG,此配置项为数组。
$this->_config=array_merge($this->_config,C('AUTH_CONFIG'));
}
}
2、检查权限:
check($name,$uid,$type=1,$mode='url',$relation='or')
大体分析一下这个方法
首先判断是否关闭权限校验如果配置信息AUTH_ON=>false则不会进行权限验证否则继续验证权限
if(!$this->_config['AUTH_ON']){
returntrue;
}
获取权限列表之后会详细介绍:
$authList=$this->getAuthList($uid,$type);
此次需要验证的规则列表转换成数组:
if(is_string($name)){
$name=strtolower($name);
if(strpos($name,',')!==false){
$name=explode(',',$name);
}else{
$name=array($name);
}
}
所以$name参数是不区分大小写的,最终都会转换成小写
开启url模式时全部转换为小写:
if($mode=='url'){
$REQUEST=unserialize(strtolower(serialize($_REQUEST)));
}
权限校验核心代码段之一,即循环所有该用户权限判断当前需要验证的权限是否在用户授权列表中:
foreach($authListas$auth){
$query=preg_replace('/^.+\?/U','',$auth);//获取url参数
if($mode=='url'&&$query!=$auth){
parse_str($query,$param);//获取数组形式url参数
$intersect=array_intersect_assoc($REQUEST,$param);
$auth=preg_replace('/\?.*$/U','',$auth);//获取访问的url文件
if(in_array($auth,$name)&&$intersect==$param){//如果节点相符且url参数满足
$list[]=$auth;
}
}elseif(in_array($auth,$name)){
$list[]=$auth;
}
}
in_array($auth,$name)如果权限列表中其中一条权限等于当前需要校验的权限则加入到$list中
注:
$list=array();//保存验证通过的规则名
if($relation=='or'and!empty($list)){
returntrue;
}
$diff=array_diff($name,$list);
if($relation=='and'andempty($diff)){
returntrue;
}
$relation=='or'and!empty($list);//当or时只要有一条是通过的则权限为真
$relation=='and'andempty($diff);//当and时$name与$list完全相等时权限为真
3、获取权限列表:
$authList=$this->getAuthList($uid,$type);//获取用户需要验证的所有有效规则列表
这个主要流程:
获取用户组
$groups=$this->getGroups($uid); //SELECT`rules`FROMthink_auth_group_accessaINNERJOINthink_auth_groupgona.group_id=g.idWHERE(a.uid='1'andg.status='1')
简化操作就是:
SELECT`rules`FROMthink_auth_groupWHERESTATUS='1'ANDid='1'//按正常流程去think_auth_group_access表中内联有点多余....!
取得用户组rules规则字段这个字段中保存的是think_auth_rule规则表的id用,分割
$ids就是$groups变量最终转换成的id数组:
$map=array(
'id'=>array('in',$ids),
'type'=>$type,
'status'=>1,
);
取得think_auth_rule表中的规则信息,之后循环:
foreach($rulesas$rule){
if(!empty($rule['condition'])){//根据condition进行验证
$user=$this->getUserInfo($uid);//获取用户信息,一维数组
$command=preg_replace('/\{(\w*?)\}/','$user[\'\\1\']',$rule['condition']);
//dump($command);//debug
@(eval('$condition=('.$command.');'));
if($condition){
$authList[]=strtolower($rule['name']);
}
}else{
//只要存在就记录
$authList[]=strtolower($rule['name']);
}
}
if(!empty($rule['condition'])){//根据condition进行验证
这里就可以明白getUserInfo会去获取配置文件AUTH_USER对应表名去查找用户信息
重点是:
$command=preg_replace('/\{(\w*?)\}/','$user[\'\\1\']',$rule['condition']);
@(eval('$condition=('.$command.');'));
'/\{(\w*?)\}/可以看成要匹配的文字为{字符串}那么{字符串}会替换成$user['字符串']
$command=$user['字符串']
如果
$rule['condition']='{age}';
$command=$user['age']
$rule['condition']='{age}>5';
$command=$user['age']>10
@(eval('$condition=('.$command.');'));
即:
$condition=($user['age']>10);
这时再看下面代码如果为真则加为授权列表
if($condition){
$authList[]=strtolower($rule['name']);
}
更多关于thinkPHP相关内容感兴趣的读者可查看本站专题:《ThinkPHP入门教程》、《thinkPHP模板操作技巧总结》、《ThinkPHP常用方法总结》、《smarty模板入门基础教程》及《PHP模板技术总结》。
希望本文所述对大家基于ThinkPHP框架的PHP程序设计有所帮助。