ThinkPHP权限认证Auth实例详解

来源:互联网 发布:godaddy域名续费好贵 编辑:程序博客网 时间:2024/06/17 06:27

本文以实例代码的形式深入剖析了ThinkPHP权限认证Auth的实现原理与方法,具体步骤如下:

mysql数据库部分sql代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
-- ----------------------------
-- Table structure for think_auth_group
-- ----------------------------
DROP TABLE IF EXISTS `think_auth_group`;
CREATE TABLE `think_auth_group` (
 `id` mediumint(8) unsignedNOTNULL AUTO_INCREMENT,
 `title`char(100)NOT NULL DEFAULT '',
 `status` tinyint(1)NOTNULL DEFAULT '1',
 `rules`char(80)NOTNULL DEFAULT '',
 PRIMARYKEY(`id`)
) ENGINE=MyISAM AUTO_INCREMENT=2DEFAULTCHARSET=utf8 COMMENT='用户组表';
 
-- ----------------------------
-- Records of think_auth_group
-- ----------------------------
INSERT INTO `think_auth_group` VALUES('1','管理组','1','1,2');
 
-- ----------------------------
-- Table structure for think_auth_group_access
-- ----------------------------
DROP TABLE IF EXISTS `think_auth_group_access`;
CREATE TABLE `think_auth_group_access` (
 `uid` mediumint(8) unsignedNOTNULL COMMENT '用户id',
 `group_id` mediumint(8) unsignedNOTNULL COMMENT '用户组id',
 UNIQUEKEY`uid_group_id` (`uid`,`group_id`),
 KEY`uid` (`uid`),
 KEY`group_id` (`group_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='用户组明细表';
 
-- ----------------------------
-- Records of think_auth_group_access
-- ----------------------------
INSERT INTO `think_auth_group_access` VALUES('1','1');
INSERT INTO `think_auth_group_access` VALUES('1','2');
 
-- ----------------------------
-- Table structure for think_auth_rule
-- ----------------------------
DROP TABLE IF EXISTS `think_auth_rule`;
CREATE TABLE `think_auth_rule` (
 `id` mediumint(8) unsignedNOTNULL AUTO_INCREMENT,
 `name`char(80)NOTNULL DEFAULT '' COMMENT'规则唯一标识',
 `title`char(20)NOTNULL DEFAULT '' COMMENT'规则中文名称',
 `status` tinyint(1)NOTNULL DEFAULT '1' COMMENT'状态:为1正常,为0禁用',
 `type`char(80)NOTNULL,
 `condition`char(100)NOT NULL DEFAULT '' COMMENT'规则表达式,为空表示存在就验证,不为空表示按照条件验证',
 PRIMARYKEY(`id`),
 UNIQUEKEY`name` (`name`)
) ENGINE=MyISAM AUTO_INCREMENT=5DEFAULTCHARSET=utf8 COMMENT='规则表';
 
-- ----------------------------
-- Records of think_auth_rule
-- ----------------------------
INSERT INTO `think_auth_rule` VALUES('1','Home/index','列表', '1','Home','');
INSERT INTO `think_auth_rule` VALUES('2','Home/add','添加', '1','Home','');
INSERT INTO `think_auth_rule` VALUES('3','Home/edit','编辑', '1','Home','');
INSERT INTO `think_auth_rule` VALUES('4','Home/delete','删除', '1','Home','');
 
 
DROP TABLE IF EXISTS `think_user`;
CREATE TABLE `think_user` (
 `id`int(11)NOTNULL,
 `username`varchar(30)DEFAULT NULL,
 `password`varchar(32)DEFAULT NULL,
 `age` tinyint(2)DEFAULTNULL,
 PRIMARYKEY(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
-- ----------------------------
-- Records of think_user
-- ----------------------------
INSERT INTO `think_user` VALUES('1','admin','21232f297a57a5a743894a0e4a801fc3','25');

配置文件Application\Common\Conf\config.php部分:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
 
return array(
 //'配置项'=>'配置值'
 '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代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
namespace Home\Controller;
use Think\Controller;
class IndexController extends Controller {
 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、权限检验类初始化配置信息:

?
1
$Auth = new \Think\Auth();

创建一个对象时程序会合并配置信息
程序会合并Application\Common\Conf\config.php中的AUTH_CONFIG数组

?
1
2
3
4
5
6
7
8
9
10
11
public function __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、检查权限:

?
1
check($name,$uid,$type= 1, $mode = 'url',$relation= 'or')

大体分析一下这个方法

首先判断是否关闭权限校验 如果配置信息AUTH_ON=>false 则不会进行权限验证 否则继续验证权限

?
1
2
3
if (!$this->_config['AUTH_ON']) {
 returntrue;
}

获取权限列表之后会详细介绍:

?
1
$authList=$this->getAuthList($uid,$type);

此次需要验证的规则列表转换成数组:

?
1
2
3
4
5
6
7
8
if (is_string($name)) {
 $name=strtolower($name);
 if(strpos($name,',') !== false) {
 $name=explode(',',$name);
 }else{
 $name=array($name);
 }
}

所以$name参数是不区分大小写的,最终都会转换成小写


开启url模式时全部转换为小写:

?
1
2
3
if ($mode == 'url') {
 $REQUEST= unserialize(strtolower(serialize($_REQUEST)));
}

权限校验核心代码段之一,即循环所有该用户权限 判断 当前需要验证的权限 是否 在用户授权列表中:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
foreach ($authList as$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中
注:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
$list = array(); //保存验证通过的规则名
 
if ($relation == 'or' and !empty($list)) {
 returntrue;
}
 
$diff = array_diff($name,$list);
if ($relation == 'and' and empty($diff)) {
 returntrue;
}
 
$relation=='or' and !empty($list);//当or时 只要有一条是通过的 则 权限为真
$relation=='and' andempty($diff);//当and时 $name与$list完全相等时 权限为真

3、获取权限列表:

?
1
$authList=$this->getAuthList($uid,$type);//获取用户需要验证的所有有效规则列表

这个主要流程:

获取用户组

?
1
2
$groups = $this->getGroups($uid);
//SELECT `rules` FROM think_auth_group_access a INNER JOIN think_auth_group g on a.group_id=g.id WHERE ( a.uid='1' and g.status='1' )

简化操作就是:

?
1
SELECT `rules` FROM think_auth_groupWHERESTATUS = '1' AND id='1'//按正常流程 去think_auth_group_access表中内联有点多余....!

取得用户组rules规则字段 这个字段中保存的是think_auth_rule规则表的id用,分割

$ids就是$groups变量最终转换成的 id数组:

?
1
2
3
4
5
$map = array(
 'id'=>array('in',$ids),
 'type'=>$type,
 'status'=> 1,
);

取得think_auth_rule表中的规则信息,之后循环:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
foreach ($rules as$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对应表名 去查找用户信息

重点是:

?
1
2
$command = preg_replace('/\{(\w*?)\}/','$user[\'\\1\']',$rule['condition']);
@(eval('$condition=('.$command . ');'));

'/\{(\w*?)\}/ 可以看成要匹配的文字为 {字符串} 那么 {字符串} 会替换成$user['字符串']
$command =$user['字符串']

如果

?
1
2
3
4
5
$rule['condition'] ='{age}';
$command =$user['age']
$rule['condition'] ='{age} > 5';
$command =$user['age'] > 10
@(eval('$condition=('.$command . ');'));

即:

?
1
$condition=($user['age'] > 10);

这时再看下面代码 如果为真则加为授权列表

?
1
2
3
if ($condition) {
  $authList[] =strtolower($rule['name']);
}

更多关于thinkPHP相关内容感兴趣的读者可查看本站专题:《ThinkPHP入门教程》、《thinkPHP模板操作技巧总结》、《ThinkPHP常用方法总结》、《smarty模板入门基础教程》及《PHP模板技术总结》。

希望本文所述对大家基于ThinkPHP框架的PHP程序设计有所帮助。

原创粉丝点击