浅谈Yii-admin的权限控制

  说到CMS,最须要有的东西就是权限控制,特别是一些复杂的场景,多用户,多角色,多部门,子父级查看等等。最近在开发一个线下销售的东东,这个系统分为管理员端,省代端,客户端,门店端,销售端, 部门端,部门老大下面分子部门等等,恶心的需求。咱们这个项目使用yii框架开发,yii在php届仍是比较流行的,虽说laravel如今横行,可是一些部门一些团队仍是采用了yii框架,好比咱们。php

  我是刚接触yii这个框架,开始的时候对这种面向组件的框架甚是别扭。当时打算本身写权限的,本身建立权限表,关联表等,可是学习使用yii开发文档后,发现有个权限控制RBAC,借助于yii-admin能够实现完美的权限,菜单的控制。这篇博客分两部门,第一部分我会讲述怎么搭建权限管理包括:安装yii-admin,建立权限表,使用权限控制菜单和访问权限等基本的操做,这部分大体说一下,想要看更详细的步骤能够参考这个比较详细的讲解:http://www.manks.top/tag/rbac.html,毕竟搭建和使用都不是难事,只要按照步骤来。第二部分我会讲解我本身的理解,包括:菜单的优化,子页面导航的选择性高亮,分角色显示菜单,权限检测的改进等。html

目录:laravel

1、yii-admin的搭建相关git

一、搭建yii-admin github

二、配置数据库权限表
web

三、进行菜单控制redis

2、yii-admin优化和重写sql

一、菜单的优化数据库

二、导航的高亮,图标,是否显示json

三、重写权限检测

 

、yii-admin的搭建相关

一、搭建yii-admin

  首先你应该安装一个yii框架,由于yii-admin是基于yii框架的,没有框架玩毛啊!你能够在github上直接下载源码

  yii2:https://github.com/yiisoft/yii2 

  yii2-admin:https://github.com/mdmsoft/yii2-admin

  固然你可使用composer来安装,这样最好不过,若是你安装好了yii,你就能够切换到项目目录下,直接执行下面的命令:

1 php composer.phar require mdmsoft/yii2-admin "~2.0"
2 php composer.phar update

  而后配置中加入yii-admin的配置项,值的注意的是若是yii2-admin配置在common目录下是全局生效,那么你在执行命令控制台的时候就会报错,因此应将权限控制做用于web模块,咱们这个项目没有使用高级模板,因此你能够直接把配置写在config下面的web.php中,配置以下:

先定义别名:

1 'aliases' => [
2     '@mdm/admin' => '@vendor/mdmsoft/yii2-admin',
3 ],

在modules中添加admin组件:

1 'admin' => [
2     'class' => 'mdm\admin\Module',
3     'layout' => '@app/views/layouts/main_nifty',//yii2-admin的导航菜单
4 ],

添加添加authManager配置项:

须要强调的是,yii中的authManager组件有PhpManager和DbManager两种方式,这两种方式是由区别的,PhpManager将权限关系保存在文件里,DbManager方式,将权限关系保存在数据库。咱们采用保存在数据库中的方式。

1 'authManager' => [
2     'class' => 'yii\rbac\DbManager', // or use 'yii\rbac\DbManager'
3 ],

添加as access:

 1 'as access' => [
 2     'class' => 'mdm\admin\components\AccessControl',
 3     'allowActions' => [
 4         // add or remove allowed actions to this list
 5         // 'admin/*',
 6         //'*',
 7         'site/*',
 8         'api/*',
 9     ]
10 ],

须要说的是未知不要放错了,以下图所示:

 

二、配置数据库权限表

这一步不用本身去写,命令行切换到yii2目录,执行下面命令,建立rbac须要的表,可是数据库须要本身建立,名字默认是yii2basic,若是要执行命令,就须要把你刚下配置好的配置文件在在console.php中也写一份,若是执行不成功,能够吧生成数据表的脚本拿出来本身执行。

1 yii migrate --migrationPath=@yii/rbac/migrations
2 yii migrate --migrationPath=@mdm/admin/migrations 

若是执行成功会生成5张表,还须要一张user表,你能够本身添加

1 menu             //菜单表
2 auth_rule        //规则表
3 auth_item_child  //角色对应的权限,parent角色,child权限名
4 auth_item        //角色、权限表,type=1表示角色,type=2表示权限
5 auth_assignment  //角色与用户对应关系表

  若是所有成功的话,访问index.php?r=admin 就能够了看到权限的控制可视化页面,若是出错,认真查看错误缘由,基本上都是配置不对。配置好的话,访问其余页面就没有权限了,而后你能够修改as access中的allowActions,这在开发api或者一些共用模块的时候颇有用,由于这些页面不须要进行权限的控制。默认风格的权限控制页面以下图:

三、进行菜单控制

  要进行菜单控制,就须要用到刚才建立的那几个表中的menu表,左侧的导航按照咱们的设计应该能够经过权限进行控制,写死的导航不能达到目的,可扩展性不强,因此菜单控制必需要支持。

须要注意的是,若是你的后台框架中用到了本身的layout,你须要本身去指定,咱们这个项目就是,有咱们本身的layout,上面再添加admin组件的时候已经添加了:

'layout' => '@app/views/layouts/main_nifty',

而后咱们操做菜单列表。添加菜单项,而后再打开layout文件,其实获取菜单的逻辑已经写好了,在MenuHelper中,添加命名空间mdm\admin\components\MenuHelper; 而后注销原来的导航,添加下面的代码,基本上就能够实现权限-用户-导航的控制了。

1 echo Nav::widget(
2     [
3         "encodeLabels" => false,
4         "options" => ["class" => "sidebar-menu"],
5         "items" => MenuHelper::getAssignedMenu(Yii::$app->user->id),
6     ]
7 );

好了说完了,最后看一下这个页面:

2、yii-admin优化和重写

  在使用的过程当中,yii-admin实现的导航权限控制远不能知足咱们的需求,而且这种组件试的开发,每一个操做是彻底独立的,好比检查权限,取菜单,取用户信息,每一个操做都须要执行SQL来进行。下面是正常的检查权限和获得菜单的sql执行过程,其实这个过程是极其费时的,当用户量比较多,菜单比较大,权限表中的数据很是多的时候是不能这样干的,使用咱们本身的sql检测工具能够看到,这个过程执行了20条之多的sql语句:

  在图中能够看出,权限检查涉及了14次的sql查询,菜单涉及了5次sql查询,如此多的sql 执行一旦上线是没有什么并发可言的。yii-admin这个组件提供了方便的权限控制,菜单控制,可是性能上面咱们不敢苟同。查看源码你就知道,这个组件在我看来是一个解耦比较高的组件,每一个成分之间能够单独的使用,这就须要每一个操做必需要有本身独立的数据库来源,说白了就须要每次都执行sql去取到想要的数据,中间不多使用连表查询,其实10条sql作的功能,在连表的状况下,一条sql就搞定了。

  像我这种人是不能忍受这么多不相关的sql执行的,因此我就在根源上面修改了yii-admin的权限检查部分,修改的方法是我本身想的,不必定对,也不必定适合全部的场景,下面就写出来与你们分享。

 

一、菜单的优化

  咱们经过查看菜单的生成过程大体会执行了5条以上的sql,这个还算能够,我没有作sql上的优化,缘由是咱们的菜单是要对应不一样的角色和子父级关系,在原来的基础上我添加了一个type来区分是哪一种角色能看到这种菜单,以及哪一种角色对应某一个菜单显示的层级关系。这样管理员,省代用户,客户都会呈现不一样的菜单。即便配置相同的权限,不一样层级的用户也会看到不一样的菜单。

  我作的优化是缓存菜单的生成数据,咱们这个菜单是定制的,没有采用一开始配置的Nav::widget来呈现,而是咱们本身循环层级关系,这样虽然麻烦,可是能很好的提取菜单中咱们须要的每个逻辑,好比:面包屑的自动生成就能够每次提取菜单的label,再好比子页面,不一样控制器下得左导航的高亮,下面是代码,php和html混写了,之后会慢慢的提取。

 1 <ul class="nav nav-list">
 2 <?php 
 3 $idx = 1;
 4 $request_url = '/' . $mod_id . '/' . $con_id . '/' . $act_id . '/';
 5 foreach ($menus_new['list'] as $label => $menu1): ?>
 6 <?php
 7     if (empty($menu1['label']) && empty($menu['url'][0])) {
 8         continue;
 9     }
10 ?>
11 <?php if(!isset($menu1['items'])):?>
12     <li class="<?php 
13             if (isset($menu1['openurl']) && strstr($menu1['openurl'], $request_url)) {
14                 echo 'active';
15                 $breadcrumb[] = $menu1['label'];
16             }
17         ?>">
18         <a href="<?php echo $menu1['url'][0] ?>">
19             <i class="menu-icon fa fa-<?php echo $menu1['icon'] ?>"></i>
20             <span class="menu-text"> <?php echo $menu1['label'] ?> </span>
21         </a>
22         <b class="arrow"></b>
23     </li>
24 <?php else:?>
25     <li class="<?php
26             if (isset($menu1['openurl']) && strstr($menu1['openurl'], $request_url)) {
27                 echo 'open';
28                 $breadcrumb[] = $menu1['label'];
29             }
30         ?>">
31         <a href="index.html"data-target="#multi-cols-<?php echo $idx ?>"class="dropdown-toggle">
32             <i class="menu-icon fa fa-<?php echo $menu1['icon'] ?>"></i>
33             <span class="menu-text"> 
34                 <?php echo $menu1['label'] ?> 
35             </span>
36             <b class="arrow fa fa-angle-down"></b>
37         </a>
38         <b class="arrow"></b>
39         <ul id="multi-cols-<?php echo $idx ?>" class="submenu">
40             <?php foreach ($menu1['items'] as $label => $menu2): ?>
41             <?php 
42                 if (empty($menu2) || !is_array($menu2)) { continue; }
43                 if(!isset($menu2['items'])):?>
44                 <li class="<?php
45                     if (isset($menu2['openurl']) && strstr($menu2['openurl'], $request_url)) {
46                         echo 'active';
47                         $breadcrumb[] = $menu2['label'];
48                     }
49                 ?>">
50                     <a href="<?php echo $menu2['url'][0] ?>">
51                         <i class="menu-icon fa fa-caret-right"></i>
52                         <?php echo $menu2['label'] ?>
53                     </a>
54                     <b class="arrow"></b>
55                 </li>
56             <?php else:?>
57                 <li class="<?php 
58                     if (isset($menu2['openurl']) && strstr($menu2['openurl'], $request_url)) {
59                         echo 'open';
60                         $breadcrumb[] = $menu2['label'];
61                     }
62                     ?>">
63                     <a href="#" class="dropdown-toggle">
64                         <i class="menu-icon fa fa-caret-right"></i>
65                         <?php echo $menu2['label'] ?>
66                         <b class="arrow fa fa-angle-down"></b>
67                     </a>
68                     <b class="arrow"></b>
69                     <ul class="submenu">
70                         <?php foreach ($menu2['items'] as $label => $url): ?>
71                         <?php if (empty($url) || !is_array($url)) { continue; } ?>
72                         <li class="<?php
73                             if (isset($url['openurl']) && strstr($url['openurl'], $request_url)) {
74                                 echo 'active';
75                                 $breadcrumb[] = $url['label'];
76                             }
77                             ?>">
78                             <a href="<?php echo $url['url'][0] ?>">
79                               <i class="menu-icon fa fa-caret-right"></i>
80                               <?php echo $url['label'] ?>
81                             </a>
82                             <b class="arrow"></b>
83                         </li>
84                         <?php endforeach ?>
85                     </ul>
86                </li>
87             <?php endif?>
88             <?php endforeach ?>
89         </ul>
90     </li>
91 <?php endif?>
92 <?php $idx++; ?>
93 <?php endforeach ?>
94 </ul>

  这个导航是我本身改了好多版总结出适合咱们本身的方案,其中$breadcrumb是控制面包屑的显示,有时间我会抽离php。我介绍的是菜单优化,如今才完成了第一步菜单的显示,说到优化我是采用缓存菜单数据的策略,就是缓存上面那个$menus_new['list'],策略以下:

  这个策略使用角色缓存数据,就是使用每一个角色的权限加上uid和环境配置取MD5后生成key,考虑到用户比较多每一个用户都缓存的话开销太大,而且用户相同权限的的比较多,特殊权限的能够特殊对待,这样省去了存储好多重复的数据,环境配置是区分线上数据和测试数据,便于咱们进行调试。

  过时机制:重要的是缓存的过时机制,缓存有了可是当菜单或者权限发生变化的时候就要更新缓存,这里咱们引入了版本的概念,能作到缓存变动的最小开销。好比菜单变化,全部人导航都应该修改,这里咱们在redis中加入一个导航版本的变量,每次读入缓存的时候都会先判断这个版本与缓存中本身存储版本是否一致,若是一致证实导航没有变化,若是不一致认为菜单有修改,导航已过时,须要从新获得缓存,这样相同的角色,只要有一我的更新了导航,其余人下次再进来的时候就会访问到最新的导航(统一角色)。这个全局的redis变量会在导航变动和权限变动的时候自动加1,保证版本的变化,这样若是有4类角色,几万人的用户,实际的数据修改只发生的4次(实际会比这个多,好比同一个角色不一样的权限,那么他对应的redis key 就不同,它须要本身去取缓存)。具体的代码实现以下:

 1 $user_id = Yii::$app->user->id;
 2 $breadcrumb = [];
 3 $menus_new['list'] = MenuHelper::getAssignedMenu($user_id);
 4 
 5 $redis_key = MenuHelper::getMenuKeyByUserId($user_id);
 6 $redis_menu = Yii::$app->redis->get($redis_key);
 7 $redis_varsion = getVersion();
 8 
 9 if (!empty($redis_menu)) {
10     $menus_new = json_decode($redis_menu, true);
11     $old_version = isset($menus_new['version']) ? $menus_new['version'] : '';
12 
13     //判断菜单的版本号,便于及时更新缓存
14     if (!isset($menus_new['list']) || empty($old_version) || intval($old_version) != $redis_varsion) {
15         $menus_new = getMenu($user_id, $redis_varsion, $redis_key);
16         $log = json_encode([
17             'user_id' => $user_id,
18             'varsion' => $redis_varsion,
19             'redis_key' => $redis_key,
20             'value' => $menus_new
21         ]);
22         writeLog($log, 'update_menu');
23     }
24 } else {
25     $menus_new = getMenu($user_id, $redis_varsion, $redis_key);
26 }
27 
28 function getMenu($user_id, $varsion, $redis_key)
29 {
30     $menus_new['list'] = MenuHelper::getAssignedMenu($user_id);
31     $menus_new['version'] = $varsion;
32     Yii::$app->redis->set($redis_key, json_encode($menus_new));
33     Yii::$app->redis->expire($redis_key, 300);
34     return $menus_new;
35 }
36 
37 //设置更新key便于时时更新redis
38 function getVersion()
39 {
40     $version_key = Yii::$app->params['redis_key']['menu_prefix'] . md5(Yii::$app->params['redis_key']['menu_version'] . Yii::$app->db->dsn);
41     $version_val = Yii::$app->redis->get($version_key);
42 
43     return empty($version_val) ? 1 : $version_val;
44 }

生成key和更新key的逻辑以下:

 1 /**
 2  * get menu one user by the id
 3  * @param  $user_id
 4  * @return key string
 5  */
 6 public static function getMenuKeyByUserId($user_id)
 7 {
 8     if (empty($user_id)) {
 9         return false;
10     }
11 
12     $list = (new \yii\db\Query())->select('**')
13                                  ->from('**')
14                                  ->where(['user_id' => $user_id])
15                                  ->all();
16 
17     if (empty($list)) {
18         return false;
19     }
20 
21     $role_str = '';
22     foreach ($list as $key => $value) {
23         $role_str .= $value['item_name'];
24     }
25 
26     $redis_key = Yii::$app->params['key'] . md5($role_str . Yii::$app->db->dsn);
27 
28     return $redis_key;
29 }
30 
31 /**
32  * 修改菜单更新状态,更新redis
33  */
34 public static function UpdateMenuVersion()
35 {
36     $version_key = Yii::$app->params['key'] . md5(Yii::$app->params['key'] . Yii::$app->db->dsn);
37     $version_val = Yii::$app->redis->get($version_key);
38 
39     if (empty($version_val)) {
40         $version_val = '1';
41     } else {
42         $version_val++;
43     }
44 
45     $log = json_encode([
46         'user_id' => Yii::$app->user->id, 
47         'version_key' => $version_key, 
48         'version_val' => $version_val
49     ]);
50     writeLog($log, 'update_menu_version');
51 
52     Yii::$app->redis->set($version_key, $version_val);
53 }

 

二、导航的高亮,图标,是否显示

  默认的导航高亮是按照模块,控制器,方法来进行直接匹配的,这样一来有一种需求没法知足,好比:A控制器下得页面下载B控制器下面高亮,这种事没法实现的,因此要修改他们高亮机制。咱们没有再采用他的高亮逻辑,而是本身实现了一个新的逻辑。我首先把要高亮的页面url加入到菜单的data里面,data是一个json数据,以下所示:

{"icon": "fa fa-home", "visible": true, "openurl":"/web/site/index/"}

  这样咱们经过openurl就能知道哪一个导航高亮,在页面中直接判断当前请求的url在不在这个openurl里面就能够,可是这样作有缺点,必需要有把高亮的页面加入到要高亮的导航里面,若是页面太多这种方式不怎么好,可是我没有想到更好的方法去解决,若是哪位大神有好的方法能够在评论中写出,很是感谢。

  图标和可见性的控制能够借助于MenuHelper中getAssignedMenu的回调方法实现,你能够在调用该方法的时候传入回调方法,我直接写的匿名方法,添加在了该方法里面,以下所示:

 1 $user_type = Yii::$app->user->identity->type;
 2 $customer_id = Yii::$app->user->identity->customer_id;
 3 
 4 $callback_func = function($menu) use ($user_type, $customer_id) {
 5     $data = json_decode($menu['data'], true);
 6     $items = $menu['children'];
 7 
 8     $return = [
 9         'label' => $menu['name'],
10         'url' => [$menu['route']],
11     ];
12 
13     $return['visible'] = isset($data['visible']) ? $data['visible'] : '';
14 
15     //菜单隐藏的逻辑
16     if (empty($return['visible'])) {
17         return false;
18     }
19 
20     $return['icon'] = isset($data['icon']) ? $data['icon'] : '';
21 
22     //控制菜单打开的逻辑
23     $return['openurl'] = isset($data['openurl']) ? $data['openurl'] : '';
24 
25     $items && $return['items'] = $items;
26     return $return;
27 };

 

三、重写权限检测

  刚才已经说了,yii-admin 的权限检测执行太费时间,执行SQL太多,因此我打算重写他的权限检查的方法,经过读源码能够看到,他们检查是经过user中的can方法调用的,而后经过mdm\admin\components\AccessControl中的beforeAction实现的,咱们能够看一下:

 1 /**
 2  * @inheritdoc
 3  */
 4 public function beforeAction($action)
 5 {
 6     $actionId = $action->getUniqueId();
 7     $user = $this->getUser();
 8 
 9     //预留系统检查权限的逻辑,一旦重写检查权限失败,调用系统检查权限的方法
10     if ($user->can('/' . $actionId)) {
11         return true;
12     }
13     $obj = $action->controller;
14     do {
15         if ($user->can('/' . ltrim($obj->getUniqueId() . '/*', '/'))) {
16             return true;
17         }
18         $obj = $obj->module;
19     } while ($obj !== null);
20 
21     $this->denyAccess($user);
22 }

  由于全权限的检查包含了子父级检查,也就是说 /admin/menu/update的权限是对/admin/menu/* 和/admin/* 和 /*均可见的,因此咱们会看到$user->can的调用会使用do -while来进行,这样就增长的检查的复杂度,执行的sql就会批量的增长,你想啊,没一个父级的检查都是一次全新的函数调用,因此最恶心的也莫过于此了,感兴趣的同窗能够去看看他的这个过程,当你本身调用这个函数检测的时候就会发现,执行的sql不是通常的多。

下面是个人重写方法,一条SQL,兼容了权限,角色,批量检查和未登陆用户的权限检查,具体实现以下:

  1 /**
  2  * 权限判断方法 (先不要使用该方法,用的系统方法,效率极低,等有时间重写以后再用)
  3  * @param string/array $permission_name 权限值(URL 或者 权限名)/批量检测能够传入数组
  4  * @param int $user 用户id,不传值会取当前的登录用户
  5  * @return boolen
  6  * @author zhaoyafei
  7  */
  8 public static function permissionCheck($permission_name, $user = 0)
  9 {
 10     //检查是否登录过
 11     if (Yii::$app->user->isGuest) {
 12         Yii::$app->response->redirect('/site/login');
 13     }
 14 
 15     if (empty($permission_name)) {
 16         return false;
 17     }
 18 
 19     if (empty($user)) {
 20         $user = Yii::$app->user->id;
 21     }
 22 
 23     //管理员权限不能直接返回true,会存在管理员type = 1分到非管理员权限的人员(有坑)
 24 
 25     //匿名方法,处理管理员返回值的状况
 26     /*$setAdminSet = function($param) use ($permission_name) {
 27         $paramtmp = $permission_name;
 28         if (is_array($paramtmp)) {
 29             if (count($paramtmp) == 1) {
 30                 return true;
 31             }
 32 
 33             $paramtmp = array_flip($paramtmp);
 34             foreach ($paramtmp as $key => &$value) {
 35                 $value = true;
 36             }
 37         } else {
 38             $paramtmp = true;
 39         }
 40         return $paramtmp;
 41     };*/
 42 
 43     //检查是不是管理员, 管理员都有权限
 44     /*if (empty($user)) {
 45         $user = Yii::$app->user->id;
 46         $user_type = Yii::$app->user->identity->type;
 47 
 48         if ($user_type == TYPE_ADMIN) {
 49             return $setAdminSet($permission_name);
 50         }
 51 
 52     } else {
 53         $user_sql = "SELECT type FROM xm_user WHERE id = :id";
 54         $user_info = Yii::$app->db->createCommand($user_sql)->bindValue(":id", $user)->queryOne();
 55         if (empty($user_info)) {
 56             return false;
 57         }
 58 
 59         if ($user_info['type'] == TYPE_ADMIN) {
 60             return $setAdminSet($permission_name);
 61         }
 62     }*/
 63 
 64     //根据用户去取权限
 65     $permission_list = [];
 66     $sql = "SELECT xc.child, xc1.child as role_name FROM xm_auth_assignment xa 
 67             INNER JOIN xm_auth_item_child xc ON xa.item_name = xc.parent
 68             LEFT JOIN xm_auth_item_child xc1 ON xc.child = xc1.parent
 69             WHERE xa.user_id = :user_id";
 70     $permission = Yii::$app->db->createCommand($sql)
 71                           ->bindValue(":user_id", $user)
 72                           ->queryAll();
 73 
 74     if (empty($permission)) {
 75         return false;
 76     }
 77 
 78     //组合权限列表
 79     foreach ($permission as $key => $value) {
 80         if (!empty($value['child']) && !in_array($value['child'], $permission_list)) {
 81             $permission_list[] = $value['child'];
 82         }
 83         if (!empty($value['role_name']) && !in_array($value['role_name'], $permission_list)) {
 84             $permission_list[] = $value['role_name'];
 85         }
 86     }
 87 
 88     //匿名方法,处理子url生成
 89     $getUrlList = function($url) {
 90         if (!strstr($url, '/')) {
 91             return [$url];
 92         }
 93 
 94         $url = '/' . trim($url, '/');
 95         $params = explode('/', $url);
 96         $param_arr = [];
 97         $param_str = [];
 98 
 99         if (!empty($params) && is_array($params)) {
100             foreach ($params as $key => $value) {
101                 if (!empty($value)) {
102                     $param_arr[] = $value;
103                 }
104             }
105         }
106 
107         if (!empty($param_arr)) {
108             $tmp_str = '';
109             $param_str[] = $url;
110             $count = count($param_arr);
111 
112             //生成子父级关系
113             for ($i = $count -1; $i >= 0; $i--) {
114                 $tmp_str =  '/' . $param_arr[$i] . $tmp_str;
115                 $chold_url = str_replace($tmp_str, '/*', $url);
116 
117                 if (!in_array($chold_url, $param_str)) {
118                     $param_str[] = $chold_url;
119                 }
120             }
121         }
122         return $param_str;
123     };
124 
125     //拼接检查数据,兼容单传和传输组的状况
126     $check_list = [];
127     if (is_array($permission_name)) {
128         foreach ($permission_name as $key => $value) {
129             $check_list[$value] = $getUrlList($value);
130         }
131     } else {
132         $check_list[$permission_name] = $getUrlList($permission_name);
133     }
134 
135     if (empty($check_list)) {
136         return false;
137     }
138 
139     //批量检查是否有权限
140     $ret = [];
141     foreach ($check_list as $key => $value) {
142         $ret[$key] = false;
143         foreach ($value as $k => $v) {
144             if (in_array($v, $permission_list)) {
145                 $ret[$key] = true;
146                 break;
147             }
148         }
149     }
150 
151     //兼容一维数组
152     if (count($ret) == 1) {
153         $ret = array_values($ret);
154         return $ret[0];
155     }
156 
157     return $ret;
158 }

权限检测的逻辑基本上写完了,用的时候能够传入单个url或者url数组,使用方法以下:

1 //重写权限的检查逻辑
2 $ret = \app\components\Common::permissionCheck($actionId);
3  if (!empty($ret)) {
4        return true;
5 }

  须要说明的是,注释掉的部分是管理员的权限检查,若是是管理员会自动返回全部的权限,可是这种不太好,由于实际状况中会分多种管理员,这样管理员不必定拥有全部的权限,若是这样不是超级管理员就不能使用,因此用的时候仍是要慎重,最好统一使用权限检查。若是感受那个SQL执行太慢能够添加缓存,缓存过时的时间和菜单过时相似,当用户的权限有变更的时候和菜单修改的时候跟新缓存。两一种解决办法是把这个方法协程单利,利用单利只是执行一次权限的查询,检查的阶段能够单独写成方法提供。

  好了,写的手疼,yii-admin的权限检查我就写这么多,很久没有更新博客了,最近在看GO语言,准备下期为写一篇关于高大上的GO相关的博客。

     写的比较急,错别字我会慢慢纠正,若是写的有错误欢迎“大婶们”指正,

注意:
一、本博客同步更新到个人我的网站:http://www.zhaoyafei.cn
二、本文属原创内容,为了尊重他人劳动,转载请注明本文地址:
相关文章
相关标签/搜索