ThinkPHP5权限控制

我在用ThinkPHP5做开发的时候发现,它没有权限类,自己写太麻烦,于是就想到了把TP3里面的权限类拿来修改使用,结果这种方法是可行的,下面记录附上修改后的Auth.php权限类

  1 <?php
  2 // +----------------------------------------------------------------------
  3 // | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
  4 // +----------------------------------------------------------------------
  5 // | Copyright (c) 2011 http://thinkphp.cn All rights reserved.
  6 // +----------------------------------------------------------------------
  7 // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8 // +----------------------------------------------------------------------
  9 // | Author: luofei614 <weibo.com/luofei614> 
 10 // +----------------------------------------------------------------------
 11 // 功能:权限类
 12 namespace app\admin\controller;
 13     /**
 14      * 权限认证类
 15      * 功能特性:
 16      * 1,是对规则进行认证,不是对节点进行认证。用户可以把节点当作规则名称实现对节点进行认证。
 17      *      $auth=new Auth();  $auth->check('规则名称','用户id')
 18      * 2,可以同时对多条规则进行认证,并设置多条规则的关系(or或者and)
 19      *      $auth=new Auth();  $auth->check('规则1,规则2','用户id','and')
 20      *      第三个参数为and时表示,用户需要同时具有规则1和规则2的权限。 当第三个参数为or时,表示用户值需要具备其中一个条件即可。默认为or
 21      * 3,一个用户可以属于多个用户组(think_auth_group_access表 定义了用户所属用户组)。我们需要设置每个用户组拥有哪些规则(think_auth_group 定义了用户组权限)
 22      *
 23      * 4,支持规则表达式。
 24      *      在think_auth_rule 表中定义一条规则时,如果type为1, condition字段就可以定义规则表达式。 如定义{score}>5  and {score}<100  表示用户的分数在5-100之间时这条规则才会通过。
 25      */
 26 
 27 //数据库
 28 /*
 29 -- ----------------------------
 30 -- think_auth_rule,规则表,
 31 -- id:主键,name:规则唯一标识, title:规则中文名称 status 状态:为1正常,为0禁用,condition:规则表达式,为空表示存在就验证,不为空表示按照条件验证
 32 -- ----------------------------
 33  DROP TABLE IF EXISTS `think_auth_rule`;
 34 CREATE TABLE `think_auth_rule` (
 35     `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
 36     `name` char(80) NOT NULL DEFAULT '',
 37     `title` char(20) NOT NULL DEFAULT '',
 38     `type` tinyint(1) NOT NULL DEFAULT '1',
 39     `status` tinyint(1) NOT NULL DEFAULT '1',
 40     `condition` char(100) NOT NULL DEFAULT '',  # 规则附加条件,满足附加条件的规则,才认为是有效的规则
 41     PRIMARY KEY (`id`),
 42     UNIQUE KEY `name` (`name`)
 43 ) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
 44 -- ----------------------------
 45 -- think_auth_group 用户组表,
 46 -- id:主键, title:用户组中文名称, rules:用户组拥有的规则id, 多个规则","隔开,status 状态:为1正常,为0禁用
 47 -- ----------------------------
 48  DROP TABLE IF EXISTS `think_auth_group`;
 49 CREATE TABLE `think_auth_group` (
 50     `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
 51     `title` char(100) NOT NULL DEFAULT '',
 52     `status` tinyint(1) NOT NULL DEFAULT '1',
 53     `rules` char(80) NOT NULL DEFAULT '',
 54     PRIMARY KEY (`id`)
 55 ) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
 56 -- ----------------------------
 57 -- think_auth_group_access 用户组明细表
 58 -- uid:用户id,group_id:用户组id
 59 -- ----------------------------
 60 DROP TABLE IF EXISTS `think_auth_group_access`;
 61 CREATE TABLE `think_auth_group_access` (
 62     `uid` mediumint(8) unsigned NOT NULL,
 63     `group_id` mediumint(8) unsigned NOT NULL,
 64     UNIQUE KEY `uid_group_id` (`uid`,`group_id`),
 65     KEY `uid` (`uid`),
 66     KEY `group_id` (`group_id`)
 67 ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
 68  */
 69 
 70 class Auth{
 71 
 72     //默认配置
 73     protected $_config = array(
 74         'auth_on'           => true,                      // 认证开关
 75         'auth_type'         => 1,                         // 认证方式,1为实时认证;2为登录认证。
 76         'auth_group'        => '__AUTH_GROUP__',        // 用户组数据表名
 77         'auth_group_access' => 'auth_group_access', // 用户-用户组关系表
 78         'auth_rule'         => 'auth_rule',         // 权限规则表
 79         'auth_user'         => 'member'             // 用户信息表
 80     );
 81 
 82     public function __construct() {
 83         $t=config('auth_config');
 84         if (config('auth_config')) {
 85             //可设置配置项 auth_config, 此配置项为数组。
 86             $this->_config = array_merge($this->_config, config('auth_config'));
 87         }
 88     }
 89 
 90     /**
 91      * 检查权限
 92      * @param name string|array  需要验证的规则列表,支持逗号分隔的权限规则或索引数组
 93      * @param uid  int           认证用户的id
 94      * @param string mode        执行check的模式
 95      * @param relation string    如果为 'or' 表示满足任一条规则即通过验证;如果为 'and'则表示需满足所有规则才能通过验证
 96      * @return boolean           通过验证返回true;失败返回false
 97      */
 98     public function check($name, $uid, $type=1, $mode='url', $relation='or') {
 99         if (!$this->_config['auth_on'])
100             return true;
101         $authList = $this->getAuthList($uid,$type); //获取用户需要验证的所有有效规则列表
102         if (is_string($name)) {
103             $name = strtolower($name);
104             if (strpos($name, ',') !== false) {
105                 $name = explode(',', $name);
106             } else {
107                 $name = array($name);
108             }
109         }
110         $list = array(); //保存验证通过的规则名
111         if ($mode=='url') {
112             $REQUEST = unserialize( strtolower(serialize($_REQUEST)) );
113         }
114         foreach ( $authList as $auth ) {
115             $query = preg_replace('/^.+\?/U','',$auth);
116             if ($mode=='url' && $query!=$auth ) {
117                 parse_str($query,$param); //解析规则中的param
118                 $intersect = array_intersect_assoc($REQUEST,$param);
119                 $auth = preg_replace('/\?.*$/U','',$auth);
120                 if ( in_array($auth,$name) && $intersect==$param ) {  //如果节点相符且url参数满足
121                     $list[] = $auth ;
122                 }
123             }else if (in_array($auth , $name)){
124                 $list[] = $auth ;
125             }
126         }
127         if ($relation == 'or' and !empty($list)) {
128             return true;
129         }
130         $diff = array_diff($name, $list);
131         if ($relation == 'and' and empty($diff)) {
132             return true;
133         }
134         return false;
135     }
136 
137     /**
138      * 根据用户id获取用户组,返回值为数组
139      * @param  uid int     用户id
140      * @return array       用户所属的用户组 array(
141      *     array('uid'=>'用户id','group_id'=>'用户组id','title'=>'用户组名称','rules'=>'用户组拥有的规则id,多个,号隔开'),
142      *     ...)
143      */
144     public function getGroups($uid) {
145         static $groups = array();
146         if (isset($groups[$uid]))
147             return $groups[$uid];
148         $user_groups = \think\Db::name($this->_config['auth_group_access'])
149             ->alias('a')
150             ->join($this->_config['auth_group']." g", "g.id=a.group_id")
151             ->where("a.uid='$uid' and g.status='1'")
152             ->field('uid,group_id,title,rules')->select();
153         $groups[$uid] = $user_groups ? $user_groups : array();
154         return $groups[$uid];
155     }
156 
157     /**
158      * 获得权限列表
159      * @param integer $uid  用户id
160      * @param integer $type
161      */
162     protected function getAuthList($uid,$type) {
163         static $_authList = array(); //保存用户验证通过的权限列表
164         $t = implode(',',(array)$type);
165         if (isset($_authList[$uid.$t])) {
166             return $_authList[$uid.$t];
167         }
168         if( $this->_config['auth_type']==2 && isset($_SESSION['_auth_list_'.$uid.$t])){
169             return $_SESSION['_auth_list_'.$uid.$t];
170         }
171 
172         //读取用户所属用户组
173         $groups = $this->getGroups($uid);
174         $ids = array();//保存用户所属用户组设置的所有权限规则id
175         foreach ($groups as $g) {
176             $ids = array_merge($ids, explode(',', trim($g['rules'], ',')));
177         }
178         $ids = array_unique($ids);
179         if (empty($ids)) {
180             $_authList[$uid.$t] = array();
181             return array();
182         }
183 
184         $map=array(
185             'id'=>array('in',$ids),
186             'type'=>$type,
187             'status'=>1,
188         );
189         //读取用户组所有权限规则
190         $rules = \think\Db::name($this->_config['auth_rule'])->where($map)->field('condition,name')->select();
191 
192         //循环规则,判断结果。
193         $authList = array();   //
194         foreach ($rules as $rule) {
195             if (!empty($rule['condition'])) { //根据condition进行验证
196                 $user = $this->getUserInfo($uid);//获取用户信息,一维数组
197 
198                 $command = preg_replace('/\{(\w*?)\}/', '$user[\'\\1\']', $rule['condition']);
199                 //dump($command);//debug
200                 @(eval('$condition=(' . $command . ');'));
201                 if ($condition) {
202                     $authList[] = strtolower($rule['name']);
203                 }
204             } else {
205                 //只要存在就记录
206                 $authList[] = strtolower($rule['name']);
207             }
208         }
209         $_authList[$uid.$t] = $authList;
210         if($this->_config['auth_type']==2){
211             //规则列表结果保存到session
212             $_SESSION['_auth_list_'.$uid.$t]=$authList;
213         }
214         return array_unique($authList);
215     }
216 
217     /**
218      * 获得用户资料,根据自己的情况读取数据库
219      */
220     protected function getUserInfo($uid) {
221         static $userinfo=array();
222         if(!isset($userinfo[$uid])){
223             $userinfo[$uid]=\think\Db::name($this->_config['auth_user'])->where(array('uid'=>$uid))->find();
224         }
225         return $userinfo[$uid];
226     }
227 
228 }

那么有了这个类,怎么用它呢?其实很简单,复制注释中的sql语句,建立三张表,然后写一个公共的类继承控制器类,然后在需要验证权限的类中继承这个公共类就行了,下面附上这个公共类Common.php

 1 <?php
 2 /**
 3  * 作者:魏安来
 4  * 功能:后台公共类,控制后台权限
 5  * 日期:2017/11/13
 6  */
 7 namespace app\admin\controller;
 8 use think\Controller;
 9 use think\Session;
10 use think\Request;
11 
12 class Common extends Controller {
13     
14     /**
15      * 初始化方法
16      * 功能:验证权限
17      */
18     public function _initialize() {
19         /*判断用户是否有Session,没有就退出登录*/
20         if(!Session::get('uid')) {
21             $this->redirect('login/index');
22         }
23 
24         $request = Request::instance();
25         $auth = new Auth();
26 
27         /*************验证权限*************/
28         $controller = $this->uncamelize(request()->controller());
29         $name = $controller.'/'.request()->action();
30         $res = $auth->check($name,Session::get('uid'));
31         //定义一个数组,放置可以不限制权限的页面
32         $noCheck = ['index/index','index/logout'];
33         //超级管理员不用验证权限
34         $superadmin = $auth->getGroups(Session::get('uid'));
35         if($superadmin[0]['group_id'] != 1) {
36             if(!in_array($name,$noCheck)) {
37                 if(!$res) {
38                     $this->error('你没有权限');
39                 }
40             }
41         }
42     }
43 
44     
45     /**
46     * 驼峰命名转下划线命名
47     * 思路:
48     * 小写和大写紧挨一起的地方,加上分隔符,然后全部转小写
49     */
50     public function uncamelize($camelCaps,$separator='_')
51     {
52         return strtolower(preg_replace('/([a-z])([A-Z])/', "$1" . $separator . "$2", $camelCaps));
53     }
54     
55 }
56 
57 ?>

 

这样就搞定了权限验证了.不过原则上没有权限不应该显示的,上面的验证方法可以阻止企图绕过权限验证的行为,比如直接在地址栏输入链接访问,所以上面应该的权限验证应该算是隐形的防线,关于没有权限的用户不显示,可以参考下面的写法,我就直接附上代码了:

 1 /**
 2      * 功能:显示菜单
 3      */
 4     public function menu() {
 5          /*获取所有菜单*/
 6         $allmenu = \think\Db::name('menu')->where(1)->select();
 7         /*查询二级菜单*/
 8         $res = db('menu')->where('pid','<>',0)->where('isshow','=',1)->order('sort asc')->select();
 9         $auth = new Auth();
10         $superadmin = $auth->getGroups(Session::get('uid'));
11         if($superadmin[0]['group_id'] != 1) {//超级管理员不需要验证权限
12             foreach($allmenu as $key=>$value) {
13                 $rules[$key] = $value['controller'].'/'.$value['method'];
14             }
15             //获取权限规则
16             $authList = $auth->getAuthList(Session::get('uid'),1);
17             //数组交集
18             $intersection = array_intersect($rules,$authList);
19             $arrtmp = array();
20             //将一维数组拼接成二维数组
21             foreach($intersection as $key=>$val){
22                 $tempArr = explode('/',$val);
23                 $arrtmp[$key]['controller'] = $tempArr[0];
24             }
25             $intersection = $arrtmp;
26             $menuArr = array();
27             //筛选符合权限的数组,重新组成左侧导航菜单数据
28             foreach($allmenu as $key=>$value){
29                 foreach($intersection as $val){
30                     if(!empty(array_intersect($val,$value))){
31                         $menuArr[$key] = $value;
32                     }
33                 }
34             }
35             $allmenu = $menuArr;
36             //筛选符合权限的数组,重新组成左侧导航菜单数据
37             foreach($res as $key=>$value){
38                 foreach($intersection as $val){
39                     if(!empty(array_intersect($val,$value))){
40                         $resArr[$key] = $value;
41                     }
42                 }
43             }
44             $res = $resArr;
45         }
46         $num = count($allmenu);
47         $this->view->assign('child_menu',$res);
48         $this->view->assign('num',$num);
49         $this->view->assign('allmenu',$allmenu);
50     }

 

转载于:https://www.cnblogs.com/walblog/p/7930048.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值