在后台开发中,对管理员的权限组(角色)的管理,以及对应的菜单按权限加载是基本的需求,之前基于 TP5
框架参考 Auth
类在上一家公司将这套系实践上线,目前由于更换工作,新公司使用的是 CI
框架,后台的这个需求依然存在,于是在 CI
上又实践一次,记录一下。
预期
这套简易的权限系统达到的预期效果:对每个管理员可以指定多个角色,每个角色分配不同的权限,权限对应每个控制器的每个对外调用的方法,菜单的跳转与权限挂钩,后台菜单根据管理员拥有的权限来加载,从而实现管理员=>多角色=>权限=>菜单的后台权限菜单管理系统。
环境:CodeIgniter 3.1.9
+ PHP 7.2
+ MariaDB 10.1.36
数据表:
管理员表:
CREATE TABLE `admin` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
`username` varchar(100) NOT NULL DEFAULT '' COMMENT '用户名',
`password` varchar(100) NOT NULL DEFAULT '' COMMENT '密码',
`status` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '状态',
`createtime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '添加时间',
`updatetime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uniq_username` (`username`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='管理员表';
权限表:
CREATE TABLE `auth_rule` (
`name` varchar(200) NOT NULL DEFAULT '' COMMENT '验证规则',
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(200) DEFAULT '' COMMENT '权限名称',
`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '1正常0关闭',
`cat_id` tinyint(2) NOT NULL DEFAULT '1' COMMENT '权限分类',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='权限表';
权限分类表:
CREATE TABLE `auth_rule_cat` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(200) DEFAULT '' COMMENT '权限分类名称',
`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '1正常0删除',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='权限分类表';
角色组表:
CREATE TABLE `et_auth_group` (
`id` int(4) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(200) NOT NULL DEFAULT '' COMMENT '角色名',
`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '1正常0禁用',
`rules` text NOT NULL COMMENT '权限id,英文逗号间隔',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='角色组';
用户角色关系表:
CREATE TABLE `auth_group_access` (
`uid` int(10) unsigned NOT NULL COMMENT '管理员id',
`group_id` int(10) unsigned NOT NULL COMMENT '角色组id'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户角色关系表';
后台菜单表:
CREATE TABLE `et_auth_menu` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`icon` varchar(50) DEFAULT '' COMMENT '菜单字体图标icon',
`title` varchar(100) NOT NULL DEFAULT '' COMMENT '菜单的中文名称',
`rule_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '对应的权限id',
`pid` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '菜单pid所属分类父级id,0表示顶级',
`url` varchar(100) DEFAULT '' COMMENT '菜单对应的uri',
`et_order` int(10) unsigned NOT NULL DEFAULT '1' COMMENT '排序',
`status` tinyint(2) NOT NULL DEFAULT '1' COMMENT '1正常0删除',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='后台菜单';
实现
对应数据表的增删改查就是基本的条件,这里只讲一下权限验证以及菜单加载的实现。
添加需要验证的权限:
封装验证类
在 libraries/common
下添加文件 Authhandler.php
,代码:
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
/**
* 权限验证
* hsu1943 20181112
*/
class Authhandler
{
protected $_CI;
// 默认配置
public $_config = array(
'AUTH_ON' => true, // 验证开关
'SUPER_ADMINISTRATOR' => 'admin', // 超级管理员用户名
);
public function __construct()
{
$this->_CI =& get_instance();
}
/**
* 权限验证
* @param int $uid 用户id
* @param array $names 待验证的权限,支持逗号分隔的权限规则或索引数组
* @param string $relation 待验证权限是否都需要满足,'or'|'and'
* @return boolean true|false
*/
public function check($uid, $names, $relation = 'or')
{
// 关闭验证跳过
if (!$this->_config['AUTH_ON']) {
return true;
}
// 超管跳过
if($_SESSION['admin']['username'] === $this->_config['SUPER_ADMINISTRATOR'])
{
return true;
}
// 获取用户权限
$authList = $this->getAuthList($uid);
// 待验证权限
if (is_string($names)) {
$names = strtolower($names);
if (strpos($names, ',') !== false) {
$names = explode(',', $names);
} else {
$names = array($names);
}
}
// 通过验证的权限数组
$list = [];
foreach ($names as $name) {
if(in_array($name, $authList))
{
$list[] = $name;
}
}
if ('or' == $relation and !empty($list)) {
return true;
}
$diff = array_diff($names, $list);
if ('and' == $relation and empty($diff)) {
return true;
}
return false;
}
/**
* 获取用户所有角色组下的权限name集合
* @param int $uid 管理员id
* @return array $authList 权限name集合
*/
public function getAuthList($uid)
{
$this->_CI->load->library('mysql/authdb');
$groups = $this->_CI->authdb->getGroupsByUID($uid);
$ids = [];
foreach ($groups as $g) {
$ids = array_merge($ids, explode(',', trim($g['rules'], ',')));
}
$ids = array_unique($ids);
$rules = $this->_CI->authdb->getRulesByIds($ids);
$_SESSION['admin']['AUTH_'.$uid] = $rules;
$authList = array_column($rules, 'name');
return $authList;
}
}
公共代码中添加验证
这里的公共代码在CI框架中有多重选择,比如 post_controller_constructor
钩子中验证,或者控制器的核心父类 core/MY_admin_controller.php
中。
我选择在后者,在公共父类中验证用户 SESSION
通过后做验证:
// 验证权限
$url_name = 'admin/' . $this->router->fetch_class() . '/' . $this->router->fetch_method();
$this->load->library('common/authhandler');
$check = $this->authhandler->check($_SESSION['admin']['id'], $url_name);
if(!$check){
show_error('Sorry, Permission denied!');
}
添加权限菜单
添加进来的权限菜单类似这样的:
菜单对应的 跳转URL
实际上跟权限中的规则是一一对应的,根据这个可以检索到对应的权限与菜单绑定。
根据用户权限加载对应菜单
用户登录成功,查询用户对应权限组,合并权限组拥有的权限,去重后得到该用户所拥有的全部权限,根据权限查询对应的菜单,然后在模版公共模块中循环加载出来就行了。
千里之外不离开,一直在用thinkphp
thinkphp不错的,目前中小企业用得最多的框架