源码下载:https://download.csdn.net/download/qq_14940627/11107673
首先创建一张包含URL的一张action表:
CREATE TABLE `action` (
`action_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`action_url` varchar(255) NOT NULL DEFAULT '0' COMMENT '功能URL',
`action_name` varchar(255) NOT NULL DEFAULT '0' COMMENT '功能名称',
`action_desc` varchar(255) DEFAULT NULL COMMENT '功能描述',
`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '-1:已删除, 0:禁止访问, 1:正常访问',
`pid` int(11) NOT NULL DEFAULT '0' COMMENT '功能父级id',
`module` varchar(255) DEFAULT NULL COMMENT '模块',
`type` tinyint(1) NOT NULL DEFAULT '0' COMMENT '功能类型:1-菜单 2-页面 3-按钮',
`icon` varchar(255) DEFAULT NULL COMMENT '功能icon图标',
`level` int(10) unsigned DEFAULT NULL COMMENT '功能等级',
`group_id` int(10) unsigned DEFAULT NULL COMMENT '功能组id',
`sort` tinyint(2) DEFAULT NULL,
`delete_time` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`action_id`)
) ENGINE=MyISAM AUTO_INCREMENT=19 DEFAULT CHARSET=utf8;
数据如下,大家可以根据自己实际情况插入:
INSERT INTO `action` VALUES ('2', 'admin/news/index', '新闻列表', null, '1', '0', 'admin', '0', null, null, null, '1', null);
INSERT INTO `action` VALUES ('3', 'admin/news/add', '新闻添加', null, '1', '2', 'admin', '0', null, null, null, '2', null);
INSERT INTO `action` VALUES ('4', 'admin/news/edit', '新闻编辑', null, '1', '2', 'admin', '0', null, null, null, '3', null);
INSERT INTO `action` VALUES ('5', 'admin/news/del', '新闻删除', null, '1', '2', 'admin', '0', null, null, null, '4', null);
INSERT INTO `action` VALUES ('6', 'admin/auth/index', '权限组列表', null, '1', '0', 'admin', '0', null, null, null, '5', null);
INSERT INTO `action` VALUES ('7', 'admin/auth/edit', '权限编辑', null, '1', '6', 'admin', '0', null, null, null, '6', null);
INSERT INTO `action` VALUES ('8', 'admin/auth/add', '添加权限组', null, '1', '6', 'admin', '0', null, null, null, '7', null);
INSERT INTO `action` VALUES ('9', 'admin/auth/del', '权限删除', null, '1', '6', 'admin', '0', null, null, null, '8', null);
INSERT INTO `action` VALUES ('10', 'admin/manager/index', '管理员列表', null, '1', '0', null, '0', null, null, null, null, null);
INSERT INTO `action` VALUES ('11', 'admin/manager/add', '添加管理员', null, '1', '10', null, '0', null, null, null, null, null);
INSERT INTO `action` VALUES ('12', 'admin/manager/edit', '编辑管理员', null, '1', '10', null, '0', null, null, null, null, null);
INSERT INTO `action` VALUES ('13', 'admin/manager/del', '删除管理员', null, '1', '10', null, '0', null, null, null, null, null);
INSERT INTO `action` VALUES ('14', 'admin/login/pwdmodify', '修改密码', null, '1', '0', null, '0', null, null, null, null, null);
INSERT INTO `action` VALUES ('15', 'admin/column/index', '栏目管理', null, '1', '0', null, '0', null, null, null, null, null);
INSERT INTO `action` VALUES ('16', 'admin/column/add', '增加栏目', null, '1', '15', null, '0', null, null, null, null, null);
INSERT INTO `action` VALUES ('17', 'admin/column/edit', '编辑栏目', null, '1', '15', null, '0', null, null, null, null, null);
INSERT INTO `action` VALUES ('18', 'admin/column/del', '删除栏目', null, '1', '15', null, '0', null, null, null, null, null);
创建一张管理员的表
CREATE TABLE `admin` (
`admin_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`admin_name` varchar(32) NOT NULL,
`pwd` varchar(255) NOT NULL,
`su_pwd` varchar(255) DEFAULT NULL,
`nicke_name` varchar(255) DEFAULT NULL,
`login_time` int(11) DEFAULT NULL,
`admin_status` tinyint(4) DEFAULT '1' COMMENT '管理员状态: -1: 删除 0: 禁用 1:正常',
`admin_sign` tinyint(1) NOT NULL COMMENT '1- 超级管理员,2-一般管理员',
`create_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '创建者ID',
`create_time` int(11) DEFAULT NULL COMMENT '创建时间',
`update_time` int(11) DEFAULT NULL COMMENT '跟新时间',
`sort` smallint(6) NOT NULL DEFAULT '99',
PRIMARY KEY (`admin_id`)
) ENGINE=MyISAM AUTO_INCREMENT=15 DEFAULT CHARSET=utf8;
创建一张角色表
CREATE TABLE `role` (
`role_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`role_name` varchar(255) NOT NULL,
`role_desc` varchar(255) DEFAULT NULL COMMENT '角色描述',
`role_pid` int(11) NOT NULL DEFAULT '0',
`role_status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '-1:删除 , 0:禁用,1正常',
`role_path` varchar(255) DEFAULT NULL COMMENT '树路径',
`admin_sign` varchar(255) DEFAULT NULL,
`create_time` int(11) DEFAULT NULL,
`update_time` int(11) DEFAULT NULL,
PRIMARY KEY (`role_id`)
) ENGINE=MyISAM AUTO_INCREMENT=26 DEFAULT CHARSET=utf8;
最后创建一张管理员和角色对应的表
CREATE TABLE `admin_role` (
`admin_id` int(10) unsigned NOT NULL DEFAULT '0',
`role_id` int(11) NOT NULL DEFAULT '0',
`create_time` int(11) DEFAULT NULL,
PRIMARY KEY (`admin_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
创建一个钩子在action之前执行
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
// 应用行为扩展定义文件
return [
// 应用初始化
'app_init' => [],
// 应用开始
'app_begin' => [],
// 模块初始化
'module_init' => [],
// 操作开始执行
'action_begin' => ['app\\behavior\\OperateBehavior'],
// 视图内容过滤
'view_filter' => [],
// 日志写入
'log_write' => [],
// 应用结束
'app_end' => [],
];
不要忘记在common.php 中把钩子加上去
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 流年 <liu21st@gmail.com>
// +----------------------------------------------------------------------
// 应用公共文件
use think\Hook;
Hook::add('action_begin','app\\behavior\\OperateBehavior');
然后开始具体的behavior:OperateBehavior.php
把所有的权限存储在Session中再来判断,当前的访问路径是否有权限
<?php
namespace app\behavior;
use think\Controller;
use think\Exception;
use think\Session;
class OperateBehavior extends Controller{
// 定义需要排除的权限路由
protected $exclude = [
'index/index/index',
'admin/login/index',
'admin/index/index',
'admin/class/getclass',
'admin/login/loginverify',
'admin/login/outlogin',
'admin/index/info',
'admin/class/getclass'
];
/**
* 权限验证
*/
public function run(&$params){
// 行为逻辑
try {
// 获取当前访问路由
$url = $this->getActionUrl();
if(empty(Session::get()) && !in_array($url,$this->exclude)){
$this->error('请先登录','/admin/login');
}
// 用户所拥有的权限路由
$auth = Session::get('auth.url') ? Session::get('auth.url'): [];
if(!in_array($url, $auth) && !in_array($url, $this->exclude)){
$this->error('无权限访问');
}
} catch (Exception $ex) {
print_r($ex);
}
}
/**
* 获取当前访问路由
* @param $Request
* @return string
*/
private function getActionUrl()
{
$module = request()->module();
$controller = request()->controller();
$action = request()->action();
$url = $module.'/'.$controller.'/'.$action;
return strtolower($url);
}
}
登录验证判断权限
<?php
namespace app\admin\controller;
use app\admin\model\AdminModel;
use think\Controller;
use think\Log;
use think\Request;
use think\response\Json;
use think\Session;
class LoginController extends Controller{
public function index(){
return $this->fetch();
}
/**
* 登录验证
* @param Request $Request
* @return Json
* @throws \think\Exception
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
public function loginVerify(Request $Request)
{
$name= input('post.name');
$pwd= input('post.pwd');
if(!$name) return json(array('code'=>0,'msg'=>'用户名不能为空'));
if(!$pwd) return json(array('code'=>0,'msg'=>'密码不能为空'));
$info = (new AdminModel)->loginVerify($name, $pwd); // 调用 AdminModel 中的 loginVerify() 验证方法
if(false === $info) return json(array('code'=>0,'msg'=>'登录错误!'));
if(-2 === $info) return json(array('code'=>-2,'msg'=>'账号不存在'));
if( 0 === $info) return json(array('code'=>0,'msg'=>'账号被禁用'));
if(-1 === $info) return json(array('code'=>-1,'msg'=>'账号被删除'));
if(-3 === $info) return json(array('code'=>-3,'msg'=>'密码不正确'));
if($info)
Log::record('login:登录成功','operate');
return json(array('code'=>1,'url'=>'/admin/index','msg'=>'登录成功!'));
}
public function outlogin(){
Session::clear();
$this->redirect('/admin/login');
}
public function pwdModify(){
$admin_info= Session::get('user_info');
if (Request::instance()->isPost()) {
$oldpwd= trim(input('post.oldpwd'));
$newpwd= trim(input('post.newpwd'));
$admin = AdminModel::adminFindById($admin_info['admin_id']);
if(md5($oldpwd) != $admin->pwd){
$this->error('原始密码错误');
}else{
AdminModel::editAdmin($admin_info['admin_id'], ['pwd'=>md5($newpwd)]);
$this->success('修改成功');
}
}
$this->assign('admin', $admin_info);
return $this->fetch();
}
}
在AdminModel 中进行权限的判断存储
<?php
namespace app\admin\model;
use think\Model;
use think\Db;
use think\Session;
class AdminModel extends Model
{
protected $table = '';
protected $pk = 'admin_id';
public static function AdminList($size=5){
return self::where('admin_status', '1')
->where('admin_id','<>', 1)->order('sort asc')->paginate($size);
}
public static function addAdmin($admin_data){
return self::create($admin_data);
}
public static function editAdmin($admin_id, $admin_data){
return self::where('admin_id', $admin_id)->update($admin_data);
}
public static function adminFindById($admin_id){
return self::where('admin_id', $admin_id)->find();
}
public static function delAdmin($admin_id){
return self::where('admin_id', $admin_id)->update(['admin_status'=>-1]);
}
/**
* 登录验证
* @param $name
* @param $pwd
* @return bool|int
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
* @throws \think\Exception
*/
public function loginVerify($name, $pwd){
if(!$name) return false;
if(!$pwd) return false;
// 定义存session时 需要删除的个人信息
$unField = ['pwd','create_time','update_time'];
$userInfo = self::where('admin_name','=', $name)->find();
if(!$userInfo) return -2;//账号不存在
if(-1 == $userInfo->admin_status) return -1;//账号被删除
if( 0 == $userInfo->admin_status) return 0;//账号被禁用
// 密码、超码 验证
if($userInfo->pwd != md5($pwd)) return -3;//密码不正确
// admin_sign:管理员标记,1 超级管理员,2 一般管理员
if(1 == $userInfo->admin_sign) {
//获取超级管理员权限
$auth = $this->_getAdminAuth();
}else{
//获取普通管理员权限
$auth = $this->_getAuth($userInfo->admin_id);
}
// 删除部分个人信息
foreach ($unField as $fKey => $fVal){
unset($userInfo[$fVal]);
}
$data['login_time'] = time();
// 更新登录状态
self::where('admin_id', $userInfo->admin_id)->update($data);
// 获取用户管理员角色名称
$roleName = $this->getUserRoleName($userInfo->admin_id);
$userInfo['roleName'] = $roleName;
// session存储个人信息
Session::set('user_info', $userInfo->toArray());
// session存储权限
Session::set('auth', $auth);
return true;
}
/**
* 获取管理员角色名称
* @param $adminId
* @return array|null|\PDOStatement|string|Model
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
public function getUserRoleName($adminId){
if(!is_numeric($adminId)) return '';
$roleNameArr = $this
->alias('a')
->join('admin_role ar', 'a.admin_id=ar.admin_id', 'LEFT')
->join('role r', 'ar.role_id=r.role_id', 'LEFT')
->where('a.admin_id',$adminId)
->field('r.role_name, r.role_id')
->find();
$roleName = empty($roleNameArr['role_name'])?'':$roleNameArr['role_name'];
return $roleName;
}
/**
* 获取超级管理员admin权限
* @return array
*/
private function _getAdminAuth(){
$action = ActionModel::where('status',1)->select();
if($action) {
$action = $action->toArray(); // 权限方法数组 $action
$menuUrl = $this->_getMenuUrl($action);
}
unset($action);
return $menuUrl ? $menuUrl : [];
}
/**
* 获取普通管理员权限
* @param $userId
* @return array|bool
*/
private function _getAuth($userId)
{
// 管理员角色权限
$roAction = Db::name('admin_role')->alias('ar')
->join('role_action ra', 'ra.role_id=ar.role_id', 'LEFT')
->join('action a', 'a.action_id=ra.action_id', 'LEFT')
->where('ar.admin_id',$userId)
->where('a.status',1)
->order('a.sort')
->field('a.*')
->select()->toArray();
$menuUrl= [];
if( $roAction ) {
$menuUrl = $this->_getMenuUrl( $roAction );
}
return $menuUrl ? $menuUrl : [];
}
/**
* 获取菜单树和url列表
* @param array $action
* @return array|bool
*/
private function _getMenuUrl(array $action){
if(empty($action)) return false;
$menu = []; // 主菜单数组
$sort = []; // 主菜单排序数组
$url = []; // 权限url数组
foreach ($action as $aKey => $aVal) {
if(1 == $aVal['type'] && !$aVal['module']){ // type=1\module=0 :主菜单 (ps:主菜单是通过点击'添加action'写入action表的)
$sort[] = $aVal['sort']; // 排序
$menu[] = $aVal; // 主菜单数组
}
$url[] = strtolower($aVal['action_url']); // 权限url数组
}
$menu=$this->_treeNode( $action);
// $menu 跟随 $sort 升序排序
// array_multisort($sort, SORT_ASC, $menu);
/* foreach ($menu as $mKey => $mVal){
$menu[$mKey]['action_url'] = 'javascript:;';
$menu[$mKey]['first'] = 1;
}*/
return array('menu'=>$menu,'url'=>$url);
}
private function _treeNode($data,$parentId = 0)
{
// 用于保存整理好的分类节点
$node = [];
// 循环所有分类
foreach ($data as $key => $value) {
// 如果当前分类的父id等于要寻找的父id则写入$node数组,并寻找当前分类id下的所有子分类
if($parentId == $value ['pid']) {
$node [$key] = $value;
$node [$key] ['child'] = $this->_treeNode($data,$value ['action_id']);
}
}
return $node;
}
}
至此一个权限基本完成,效果如下: