說到CMS,最須要有的東西就是權限控制,特別是一些複雜的場景,多用戶,多角色,多部門,子父級查看等等。最近在開發一個線下銷售的東東,這個系統分爲管理員端,省代端,客戶端,門店端,銷售端, 部門端,部門老大下面分子部門等等,噁心的需求。咱們這個項目使用yii框架開發,yii在php屆仍是比較流行的,雖說laravel如今橫行,可是一些部門一些團隊仍是採用了yii框架,好比咱們。php
我是剛接觸yii這個框架,開始的時候對這種面向組件的框架甚是彆扭。當時打算本身寫權限的,本身建立權限表,關聯表等,可是學習使用yii開發文檔後,發現有個權限控制RBAC,藉助於yii-admin能夠實現完美的權限,菜單的控制。這篇博客分兩部門,第一部分我會講述怎麼搭建權限管理包括:安裝yii-admin,建立權限表,使用權限控制菜單和訪問權限等基本的操做,這部分大體說一下,想要看更詳細的步驟能夠參考這個比較詳細的講解:http://www.manks.top/tag/rbac.html,畢竟搭建和使用都不是難事,只要按照步驟來。第二部分我會講解我本身的理解,包括:菜單的優化,子頁面導航的選擇性高亮,分角色顯示菜單,權限檢測的改進等。html
目錄:laravel
一、搭建yii-admin github
二、配置數據庫權限表
web
三、進行菜單控制redis
一、菜單的優化數據庫
二、導航的高亮,圖標,是否顯示json
首先你應該安裝一個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 );
好了說完了,最後看一下這個頁面:
在使用的過程當中,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相關的博客。
寫的比較急,錯別字我會慢慢糾正,若是寫的有錯誤歡迎「大嬸們」指正,