当前位置: 移动技术网 > IT编程>开发语言>PHP > ThinkPHP权限认证Auth实例详解

ThinkPHP权限认证Auth实例详解

2019年03月20日  | 移动技术网IT编程  | 我要评论

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

mysql数据库部分sql代码:

-- ----------------------------
-- table structure for think_auth_group
-- ----------------------------
drop table if exists `think_auth_group`;
create table `think_auth_group` (
 `id` mediumint(8) unsigned not null auto_increment,
 `title` char(100) not null default '',
 `status` tinyint(1) not null default '1',
 `rules` char(80) not null default '',
 primary key (`id`)
) engine=myisam auto_increment=2 default charset=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) unsigned not null comment '用户id',
 `group_id` mediumint(8) unsigned not null comment '用户组id',
 unique key `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) unsigned not null auto_increment,
 `name` char(80) not null default '' comment '规则唯一标识',
 `title` char(20) not null default '' comment '规则中文名称',
 `status` tinyint(1) not null default '1' comment '状态:为1正常,为0禁用',
 `type` char(80) not null,
 `condition` char(100) not null default '' comment '规则表达式,为空表示存在就验证,不为空表示按照条件验证',
 primary key (`id`),
 unique key `name` (`name`)
) engine=myisam auto_increment=5 default charset=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) not null,
 `username` varchar(30) default null,
 `password` varchar(32) default null,
 `age` tinyint(2) default null,
 primary key (`id`)
) engine=innodb default charset=utf8;

-- ----------------------------
-- records of think_user
-- ----------------------------
insert into `think_user` values ('1', 'admin', '21232f297a57a5a743894a0e4a801fc3', '25');

配置文件application\common\conf\config.php部分:

<?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代码:

<?php
namespace home\controller;
use think\controller;
class indexcontroller extends controller {
 public function index() {
  $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数组

 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、检查权限:

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

大体分析一下这个方法

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

if (!$this->_config['auth_on']) {
 return true;
}

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

$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 ($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;
 }
 } else if (in_array($auth, $name)) {
 $list[] = $auth;
 }
}

in_array($auth, $name) 如果 权限列表中 其中一条权限 等于 当前需要校验的权限 则加入到$list中
注:

$list = array(); //保存验证通过的规则名

if ($relation == 'or' and !empty($list)) {
 return true;
}

$diff = array_diff($name, $list);
if ($relation == 'and' and empty($diff)) {
 return true;
}

$relation == 'or' and !empty($list); //当or时 只要有一条是通过的 则 权限为真
$relation == 'and' and empty($diff); //当and时 $name与$list完全相等时 权限为真

3、获取权限列表:

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

这个主要流程:

获取用户组

$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' )

简化操作就是:

select `rules` from think_auth_group where status = '1' and id='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 ($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对应表名 去查找用户信息

重点是:

$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程序设计有所帮助。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网