深入浅析Yii admin的权限控制(4)

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

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

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

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

$user_type = Yii::$app->user->identity->type; $customer_id = Yii::$app->user->identity->customer_id; $callback_func = function($menu) use ($user_type, $customer_id) { $data = json_decode($menu['data'], true); $items = $menu['children']; $return = [ 'label' => $menu['name'], 'url' => [$menu['route']], ]; $return['visible'] = isset($data['visible']) ? $data['visible'] : ''; //菜单隐藏的逻辑 if (empty($return['visible'])) { return false; } $return['icon'] = isset($data['icon']) ? $data['icon'] : ''; //控制菜单打开的逻辑 $return['openurl'] = isset($data['openurl']) ? $data['openurl'] : ''; $items && $return['items'] = $items; return $return; };

3、重写权限检测

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

/** * @inheritdoc */ public function beforeAction($action) { $actionId = $action->getUniqueId(); $user = $this->getUser(); //预留系统检查权限的逻辑,一旦重写检查权限失败,调用系统检查权限的方法 if ($user->can('https://www.jb51.net/' . $actionId)) { return true; } $obj = $action->controller; do { if ($user->can('https://www.jb51.net/' . ltrim($obj->getUniqueId() . '/*', 'https://www.jb51.net/'))) { return true; } $obj = $obj->module; } while ($obj !== null); $this->denyAccess($user); }

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

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

/** * 权限判断方法 (先不要使用该方法,用的系统方法,效率极低,等有时间重写之后再用) * @param string/array $permission_name 权限值(URL 或者 权限名)/批量检测可以传入数组 * @param int $user 用户id,不传值会取当前的登陆用户 * @return boolen * @author zhaoyafei */ public static function permissionCheck($permission_name, $user = 0) { //检查是否登陆过 if (Yii::$app->user->isGuest) { Yii::$app->response->redirect('/site/login'); } if (empty($permission_name)) { return false; } if (empty($user)) { $user = Yii::$app->user->id; } //管理员权限不能直接返回true,会存在管理员type = 1分到非管理员权限的人员(有坑) //匿名方法,处理管理员返回值的情况 /*$setAdminSet = function($param) use ($permission_name) { $paramtmp = $permission_name; if (is_array($paramtmp)) { if (count($paramtmp) == 1) { return true; } $paramtmp = array_flip($paramtmp); foreach ($paramtmp as $key => &$value) { $value = true; } } else { $paramtmp = true; } return $paramtmp; };*/ //检查是否是管理员, 管理员都有权限 /*if (empty($user)) { $user = Yii::$app->user->id; $user_type = Yii::$app->user->identity->type; if ($user_type == TYPE_ADMIN) { return $setAdminSet($permission_name); } } else { $user_sql = "SELECT type FROM xm_user WHERE id = :id"; $user_info = Yii::$app->db->createCommand($user_sql)->bindValue(":id", $user)->queryOne(); if (empty($user_info)) { return false; } if ($user_info['type'] == TYPE_ADMIN) { return $setAdminSet($permission_name); } }*/ //根据用户去取权限 $permission_list = []; $sql = "SELECT xc.child, xc1.child as role_name FROM xm_auth_assignment xa INNER JOIN xm_auth_item_child xc ON xa.item_name = xc.parent LEFT JOIN xm_auth_item_child xc1 ON xc.child = xc1.parent WHERE xa.user_id = :user_id"; $permission = Yii::$app->db->createCommand($sql) ->bindValue(":user_id", $user) ->queryAll(); if (empty($permission)) { return false; } //组合权限列表 foreach ($permission as $key => $value) { if (!empty($value['child']) && !in_array($value['child'], $permission_list)) { $permission_list[] = $value['child']; } if (!empty($value['role_name']) && !in_array($value['role_name'], $permission_list)) { $permission_list[] = $value['role_name']; } } //匿名方法,处理子url生成 $getUrlList = function($url) { if (!strstr($url, 'https://www.jb51.net/')) { return [$url]; } $url = 'https://www.jb51.net/' . trim($url, 'https://www.jb51.net/'); $params = explode('https://www.jb51.net/', $url); $param_arr = []; $param_str = []; if (!empty($params) && is_array($params)) { foreach ($params as $key => $value) { if (!empty($value)) { $param_arr[] = $value; } } } if (!empty($param_arr)) { $tmp_str = ''; $param_str[] = $url; $count = count($param_arr); //生成子父级关系 for ($i = $count -1; $i >= 0; $i--) { $tmp_str = 'https://www.jb51.net/' . $param_arr[$i] . $tmp_str; $chold_url = str_replace($tmp_str, '/*', $url); if (!in_array($chold_url, $param_str)) { $param_str[] = $chold_url; } } } return $param_str; }; //拼接检查数据,兼容单传和传输组的情况 $check_list = []; if (is_array($permission_name)) { foreach ($permission_name as $key => $value) { $check_list[$value] = $getUrlList($value); } } else { $check_list[$permission_name] = $getUrlList($permission_name); } if (empty($check_list)) { return false; } //批量检查是否有权限 $ret = []; foreach ($check_list as $key => $value) { $ret[$key] = false; foreach ($value as $k => $v) { if (in_array($v, $permission_list)) { $ret[$key] = true; break; } } } //兼容一维数组 if (count($ret) == 1) { $ret = array_values($ret); return $ret[0]; } return $ret; }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/a50fdab610fa5c94ec2ae4d63bbd7bd4.html