找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 60|回复: 0

[Thinkphp] thinkphp6.*权限管理

[复制链接] IP属地:广东省广州市
发表于 2023-7-4 23:09:11 | 显示全部楼层 |阅读模式
  1. <?php
  2. use think\facade\Request;
  3. use think\facade\Session;
  4. use think\facade\Db;
  5. use think\facade\Config;
  6. /**
  7. * 权限认证类
  8. * 功能特性:
  9. * 1,是对规则进行认证,不是对节点进行认证。用户可以把节点当作规则名称实现对节点进行认证。
  10. *   $auth = new Auth();
  11. *   $auth->check('规则名称', '用户id');
  12. *
  13. * 2,可以同时对多条规则进行认证,并设置多条规则的关系(or或者and)
  14. *   $auth = new Auth();
  15. *   $auth->check('规则1,规则2', '用户id', 'and');
  16. *   第三个参数为and时表示,用户需要同时具有规则1和规则2的权限。 当第三个参数为or时,表示用户值需要具备其中一个条件即可。默认为or
  17. * 3,一个用户可以属于多个用户组(think_auth_group_access表 定义了用户所属用户组)。我们需要设置每个用户组拥有哪些规则(think_auth_group 定义了用户组权限)
  18. *
  19. * 4,支持规则表达式。
  20. *   在 think_auth_rule 表中定义一条规则时,如果type为1, condition字段就可以定义规则表达式。 如定义{score}>5  and {score}<100  表示用户的分数在5-100之间时这条规则才会通过。
  21. */
  22. // 数据库
  23. /*
  24. -- ----------------------------
  25. -- think_auth_rule,规则表,
  26. -- id:主键,name:规则唯一标识, title:规则中文名称 status 状态:为1正常,为0禁用,condition:规则表达式,为空表示存在就验证,不为空表示按照条件验证
  27. -- ----------------------------
  28. DROP TABLE IF EXISTS `think_auth_rule`;
  29. CREATE TABLE `think_auth_rule` (
  30.     `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
  31.     `name` char(80) NOT NULL DEFAULT '',
  32.     `title` char(20) NOT NULL DEFAULT '',
  33.     `type` tinyint(1) NOT NULL DEFAULT '1',
  34.     `status` tinyint(1) NOT NULL DEFAULT '1',
  35.     `condition` char(100) NOT NULL DEFAULT '',  # 规则附件条件,满足附加条件的规则,才认为是有效的规则
  36.     PRIMARY KEY (`id`),
  37.     UNIQUE KEY `name` (`name`)
  38. ) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
  39. -- ----------------------------
  40. -- think_auth_group 用户组表,
  41. -- id:主键, title:用户组中文名称, rules:用户组拥有的规则id, 多个规则","隔开,status 状态:为1正常,为0禁用
  42. -- ----------------------------
  43. DROP TABLE IF EXISTS `think_auth_group`;
  44. CREATE TABLE `think_auth_group` (
  45.     `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
  46.     `title` char(100) NOT NULL DEFAULT '',
  47.     `status` tinyint(1) NOT NULL DEFAULT '1',
  48.     `rules` char(80) NOT NULL DEFAULT '',
  49.     PRIMARY KEY (`id`)
  50. ) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
  51. -- ----------------------------
  52. -- think_auth_group_access 用户组明细表
  53. -- uid:用户id,group_id:用户组id
  54. -- ----------------------------
  55. DROP TABLE IF EXISTS `think_auth_group_access`;
  56. CREATE TABLE `think_auth_group_access` (
  57.     `uid` mediumint(8) unsigned NOT NULL,
  58.     `group_id` mediumint(8) unsigned NOT NULL,
  59.     UNIQUE KEY `uid_group_id` (`uid`,`group_id`),
  60.     KEY `uid` (`uid`),
  61.     KEY `group_id` (`group_id`)
  62. ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
  63. */
  64. class Auth
  65. {
  66.     protected $_config = [
  67.         'auth_on'           =>  true,                // 认证开关
  68.         'auth_type'         =>  1,                   // 认证方式,1为实时认证;2为登录认证。
  69.         'auth_group'        =>  'auth_group',        // 用户组数据表名
  70.         'auth_group_access' =>  'auth_group_access', // 用户-用户组关系表
  71.         'auth_rule'         =>  'auth_rule',         // 权限规则表
  72.         'auth_user'         =>  'admin',             // 用户信息表
  73.         'auth_user_id_field'=>  'id',                // 用户表ID字段名
  74.     ];
  75.     protected $BreadCrumb = [];
  76.     public function __construct()
  77.     {
  78.         if (Config::get('app.auth')) {
  79.             $this->_config = array_merge($this->_config, Config::get('app.auth'));
  80.         }
  81.     }
  82.     /**
  83.      * 检查权限
  84.      * @param  string|array  $name     需要验证的规则列表,支持逗号分隔的权限规则或索引数组
  85.      * @param  integer  $uid      认证用户ID
  86.      * @param  string   $relation 如果为 'or' 表示满足任一条规则即通过验证;如果为 'and' 则表示需满足所有规则才能通过验证
  87.      * @param  string   $mode     执行check的模式
  88.      * @param  integer  $type     规则类型
  89.      * @return boolean           通过验证返回true;失败返回false
  90.      */
  91.     public function check($name, $uid, $relation = 'or', $mode = 'url', $type = 1)
  92.     {
  93.         if (!$this->_config['auth_on']) {
  94.             return true;
  95.         }
  96.         $authList = $this->getAuthList($uid, $type);
  97.         if (is_string($name)) {
  98.             $name = strtolower($name);
  99.             if (strpos($name, ',') !== false) {
  100.                 $name = explode(',', $name);
  101.             } else {
  102.                 $name = [$name];
  103.             }
  104.         }
  105.         $list = [];
  106.         if ($mode === 'url') {
  107.             $REQUEST = unserialize(strtolower(serialize($_REQUEST)));
  108.         }
  109.         foreach ($authList as $auth) {

  110.             $query = preg_replace('/^.+\?/U', '', $auth);
  111.             if ($mode === 'url' && $query != $auth) {
  112.                 parse_str($query, $param); // 解析规则中的param
  113.                 $intersect = array_intersect_assoc($REQUEST, $param);
  114.                 $auth = preg_replace('/\?.*$/U', '', $auth);
  115.                 if (in_array($auth, $name) && $intersect == $param) {
  116.                     $list[] = $auth;
  117.                 }
  118.             } elseif (in_array($auth, $name)) {
  119.                 $list[] = $auth;
  120.             }
  121.         }
  122.         if ($relation === 'or' && !empty($list)) {
  123.             return true;
  124.         }
  125.         $diff = array_diff($name, $list);
  126.         if ($relation === 'and' && empty($diff)) {
  127.             return true;
  128.         }
  129.         return false;
  130.     }

  131.     /**
  132.      * 根据用户ID获取用户组,返回值为数组
  133.      * @param  integer $uid 用户ID
  134.      * @return array      用户所属用户组 ['uid'=>'用户ID', 'group_id'=>'用户组ID', 'title'=>'用户组名', 'rules'=>'用户组拥有的规则ID,多个用英文,隔开']
  135.      */
  136.     public function getGroups($uid)
  137.     {
  138.         static $groups = [];
  139.         if (isset($groups[$uid])) {
  140.             return $groups[$uid];
  141.         }
  142.         $user_groups = Db::name($this->_config['auth_group_access'])
  143.             ->alias('a')
  144.             ->where('a.uid', $uid)
  145.             ->where('g.status', 1)
  146.             ->join($this->_config['auth_group'].' g', "a.group_id = g.id")
  147.             ->field('uid,group_id,title,rules')
  148.             ->select();
  149.         $groups[$uid] = $user_groups ?: [];
  150.         return $groups[$uid];
  151.     }

  152.     /**
  153.      * 获得权限列表
  154.      * @param  integer $uid  用户ID
  155.      * @param  integer $type 规则类型
  156.      * @return array       权限列表
  157.      */
  158.     protected function getAuthList($uid, $type)
  159.     {
  160.         static $_authList = [];
  161.         $t = implode(',', (array)$type);
  162.         if (isset($_authList[$uid.$t])) {
  163.             return $_authList[$uid.$t];
  164.         }
  165.         if ($this->_config['auth_type'] == 2 && Session::has('_AUTH_LIST_'.$uid.$t)) {
  166.             return Session::get('_AUTH_LIST_'.$uid.$t);
  167.         }
  168.         // 读取用户所属用户组
  169.         $groups = $this->getGroups($uid);
  170.         $ids = []; // 保存用户所属用户组设置的所有权限规则ID
  171.         foreach ($groups as $g) {
  172.             $ids = array_merge($ids, explode(',', trim($g['rules'], ',')));
  173.         }
  174.         $ids = array_unique($ids);
  175.         if (empty($ids)) {
  176.             $_authList[$uid.$t] = [];
  177.             return [];
  178.         }
  179.         $map = [
  180.             ['id', 'in', $ids],
  181.             ['type', '=', $type],
  182.             ['auth_open', '=', 1]
  183.         ];
  184.         // 读取用户组所有权限规则
  185.         $rules = Db::name($this->_config['auth_rule'])->where($map)->field('condition,name')->select();
  186.         // 循环规则,判断结果。
  187.         $authList = [];
  188.         foreach ($rules as $rule) {
  189.             if (!empty($rule['condition'])) { // 根据condition进行验证
  190.                 $user = $this->getUserInfo($uid); // 获取用户信息,一维数组
  191.                 $command = preg_replace('/\{(\w*?)\}/', '$user[\'\\1\']', $rule['condition']);
  192.                 // dump($command); // debug
  193.                 //@(eval('$condition=('.$command.');'));
  194.                 if ($condition) {
  195.                     $authList[] = strtolower($rule['name']);
  196.                 }
  197.             } else {
  198.                 // 只要存在就记录
  199.                 $authList[] = strtolower($rule['name']);
  200.             }
  201.         }
  202.         $_authList[$uid.$t] = $authList;
  203.         if ($this->_config['auth_type'] == 2) {
  204.             Session::set('_AUTH_LIST_'.$uid.$t, $authList);
  205.         }
  206.         return array_unique($authList);
  207.     }

  208.     /**
  209.      * 获得用户资料,根据自己的情况读取数据库
  210.      */
  211.     protected function getUserInfo($uid) {
  212.         static $userinfo = [];
  213.         if (!isset($userinfo[$uid])) {
  214.             $userinfo[$uid] = Db::name($this->_config['auth_user'])->where((string)$this->_config['auth_user_id_field'], $uid)->find();
  215.         }
  216.         return $userinfo[$uid];
  217.     }

  218.     /**
  219.      * 获得面包导航
  220.      * @param string $path
  221.      * @return array
  222.      */
  223.     public function getBreadCrumb($route = '')
  224.     {
  225.         //当前URL
  226.         $route = $route ? $route : Request::controller() . '/' . lcfirst(Request::action());

  227.         //查找名称
  228.         $data = Db::name('auth_rule')->where('name', '=', $route)->find();

  229.         $result = [];
  230.         if ($data) {
  231.             $result[] = [
  232.                 'url'   => $data['name'],
  233.                 'title' => $data['title'],
  234.                 'icon'  => $data['icon'],
  235.             ];
  236.             //查找是否有上级别
  237.             if ($data['pid']) {
  238.                 //查询上级url
  239.                 $route = Db::name('auth_rule')->where('id', '=', $data['pid'])->find();
  240.                 $crumb = $this->getBreadCrumb($route['name']);
  241.                 foreach ($crumb as $k => $v) {
  242.                     $result[] = [
  243.                         'url'   => $crumb[$k]['url'],
  244.                         'title' => $crumb[$k]['title'],
  245.                         'icon'  => $crumb[$k]['icon']
  246.                     ];
  247.                 }
  248.             }
  249.         } else {
  250.             //不存在的记录
  251.             if ($route == 'Index/index') {
  252.                 $result[] = [
  253.                     'url'   => 'Index/index',
  254.                     'title' => '控制台',
  255.                     'icon'  => 'fa fa-dashboard',
  256.                 ];
  257.             }
  258.         }
  259.         return $result;
  260.     }
  261. }
复制代码


您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|西兴社区 ( 蜀ICP备2022005627号 )|网站地图

GMT+8, 2024-9-17 04:26 , Processed in 0.610694 second(s), 23 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表