深入浅析Yii admin的权限控制

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

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

一、yii-admin的搭建相关

1、搭建yii-admin

  首先你应该安装一个yii矿建,因为yii-admin是基于yii框架的,没有框架玩毛啊!你可以在github上直接下载源码

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

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

  当然你可以使用composer来安装,这样最好不过,如果你安装好了yii,你就可以切换到项目目录下,直接执行下面的命令:

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

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

先定义别名:

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

在modules中添加admin组件:

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

添加添加authManager配置项:

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

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

添加as access:

'as access' => [
'class' => 'mdm\admin\components\AccessControl',
'allowActions' => [
// add or remove allowed actions to this list
// 'admin/*',
//'*',
'site/*',
'api/*',
]
],

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

2、配置数据库权限表

这一步不用自己去写,命令行切换到yii2目录,执行下面命令,创建rbac需要的表,但是数据库需要自己创建名字是:yii2basic,如果要执行命令,就需要把你刚下配置好的配置文件在在console.php中也写一份,如果执行不成功,可以吧生成数据表的脚本拿出来自己执行。

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

如果执行成功会生成5张表,还需要一张user表,你可以自己添加

menu //菜单表

auth_rule //规则表

auth_item_child //角色对应的权限,parent角色,child权限名

auth_item //角色、权限表,type=1表示角色,type=2表示权限

auth_assignment //角色与用户对应关系表

  如果全部成功的话,再访问index.php?r=admin 就可以了看到权限的控制可视化页面,如果出错,你认真查看错误原因,基本上都是配置不对。配置好的话,访问其他页面就没有权限了,然后你可以修改as access中的allowActions,在开发api或者一些共用的模块的时候很有用,因为这些页面不需要进行权限的控制。

权限控制页面如下图:

3、进行菜单控制

  要进行菜单控制,就需要用到刚才创建的那几个表中的menu表,左侧的导航按照我们的设计应该可以通过权限进行控制,写死的导航不能达到目的,可扩展行还不强,所以菜单控制必须要支持。

需要注意的是,如果你的后台框架中用到了自己的layout,你需要自己去指定,我们这个项目就是,有我们自己的layout,上面再添加admin组件的时候已经添加了:

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

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

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

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

二、yii-admin优化和重写

  在使用的过程中,yii-admin实现的导航权限控制远不能满足我们的需求,并且,这种组件试的开发,每个操作是完全独立的,比如,检查权限,取菜单,取用户信息,每个操作都需要执行SQL来进行下面是正常的检查权限和得到菜单的sql执行过程。其实这个过程是极其费时的,当用户量比较多,菜单比较大,权限表中的数据非常多的时候是不能这样干的,使用我们自己的sql检测工具可以看到,这个过程执行了20条之多的sql语句:

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

  像我这种人是不能忍受这么多不相关的sql执行的,所以我就在根源上面修改了yii-admin的权限检查部分,修改的方法是我自己想的,不一定对,也不一定适合所有的场景,下面就写出来与大家分享。

1、菜单的优化

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

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

<ul class="nav nav-list">
<?php
$idx = ;
$request_url = '/' . $mod_id . '/' . $con_id . '/' . $act_id . '/';
foreach ($menus_new['list'] as $label => $menu): ?>
<?php
if (empty($menu['label']) && empty($menu['url'][])) {
continue;
}
?>
<?php if(!isset($menu['items'])):?>
<li class="<?php
if (isset($menu['openurl']) && strstr($menu['openurl'], $request_url)) {
echo 'active';
$breadcrumb[] = $menu['label'];
}
?>">
<a href="<?php echo $menu['url'][] ?>">
<i class="menu-icon fa fa-<?php echo $menu['icon'] ?>"></i>
<span class="menu-text"> <?php echo $menu['label'] ?> </span>
</a>
<b class="arrow"></b>
</li>
<?php else:?>
<li class="<?php
if (isset($menu['openurl']) && strstr($menu['openurl'], $request_url)) {
echo 'open';
$breadcrumb[] = $menu['label'];
}
?>">
<a href="index.html"data-target="#multi-cols-<?php echo $idx ?>"class="dropdown-toggle">
<i class="menu-icon fa fa-<?php echo $menu['icon'] ?>"></i>
<span class="menu-text">
<?php echo $menu['label'] ?>
</span>
<b class="arrow fa fa-angle-down"></b>
</a>
<b class="arrow"></b>
<ul id="multi-cols-<?php echo $idx ?>" class="submenu">
<?php foreach ($menu['items'] as $label => $menu): ?>
<?php
if (empty($menu) || !is_array($menu)) { continue; }
if(!isset($menu['items'])):?>
<li class="<?php
if (isset($menu['openurl']) && strstr($menu['openurl'], $request_url)) {
echo 'active';
$breadcrumb[] = $menu['label'];
}
?>">
<a href="<?php echo $menu['url'][] ?>">
<i class="menu-icon fa fa-caret-right"></i>
<?php echo $menu['label'] ?>
</a>
<b class="arrow"></b>
</li>
<?php else:?>
<li class="<?php
if (isset($menu['openurl']) && strstr($menu['openurl'], $request_url)) {
echo 'open';
$breadcrumb[] = $menu['label'];
}
?>">
<a href="#" class="dropdown-toggle">
<i class="menu-icon fa fa-caret-right"></i>
<?php echo $menu['label'] ?>
<b class="arrow fa fa-angle-down"></b>
</a>
<b class="arrow"></b>
<ul class="submenu">
<?php foreach ($menu['items'] as $label => $url): ?>
<?php if (empty($url) || !is_array($url)) { continue; } ?>
<li class="<?php
if (isset($url['openurl']) && strstr($url['openurl'], $request_url)) {
echo 'active';
$breadcrumb[] = $url['label'];
}
?>">
<a href="<?php echo $url['url'][] ?>">
<i class="menu-icon fa fa-caret-right"></i>
<?php echo $url['label'] ?>
</a>
<b class="arrow"></b>
</li>
<?php endforeach ?>
</ul>
 </li>
<?php endif?>
<?php endforeach ?>
</ul>
</li>
<?php endif?>
<?php $idx++; ?>
<?php endforeach ?>
</ul>

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

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

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

$user_id = Yii::$app->user->id;
$breadcrumb = [];
$menus_new['list'] = MenuHelper::getAssignedMenu($user_id);
$redis_key = MenuHelper::getMenuKeyByUserId($user_id);
$redis_menu = Yii::$app->redis->get($redis_key);
$redis_varsion = getVersion();
if (!empty($redis_menu)) {
$menus_new = json_decode($redis_menu, true);
$old_version = isset($menus_new['version']) ? $menus_new['version'] : '';
//判断菜单的版本号,便于及时更新缓存
if (!isset($menus_new['list']) || empty($old_version) || intval($old_version) != $redis_varsion) {
$menus_new = getMenu($user_id, $redis_varsion, $redis_key);
$log = json_encode([
'user_id' => $user_id,
'varsion' => $redis_varsion,
'redis_key' => $redis_key,
'value' => $menus_new
]);
writeLog($log, 'update_menu');
}
} else {
$menus_new = getMenu($user_id, $redis_varsion, $redis_key);
}
function getMenu($user_id, $varsion, $redis_key)
{
$menus_new['list'] = MenuHelper::getAssignedMenu($user_id);
$menus_new['version'] = $varsion;
Yii::$app->redis->set($redis_key, json_encode($menus_new));
Yii::$app->redis->expire($redis_key, 300);
return $menus_new;
}
//设置更新key便于时时更新redis
function getVersion()
{
$version_key = Yii::$app->params['redis_key']['menu_prefix'] . md5(Yii::$app->params['redis_key']['menu_version'] . Yii::$app->db->dsn);
$version_val = Yii::$app->redis->get($version_key);
return empty($version_val) ? 1 : $version_val;
}
生成key和更新key的逻辑如下:
/**
* get menu one user by the id
* @param $user_id
* @return key string
*/
public static function getMenuKeyByUserId($user_id)
{
if (empty($user_id)) {
return false;
}
$list = (new \yii\db\Query())->select('**')
->from('**')
->where(['user_id' => $user_id])
->all();
if (empty($list)) {
return false;
}
$role_str = '';
foreach ($list as $key => $value) {
$role_str .= $value['item_name'];
}
$redis_key = Yii::$app->params['key'] . md5($role_str . Yii::$app->db->dsn);
return $redis_key;
}
/**
* 修改菜单更新状态,更新redis
*/
public static function UpdateMenuVersion()
{
$version_key = Yii::$app->params['key'] . md5(Yii::$app->params['key'] . Yii::$app->db->dsn);
$version_val = Yii::$app->redis->get($version_key);
if (empty($version_val)) {
$version_val = '1';
} else {
$version_val++;
}
$log = json_encode([
'user_id' => Yii::$app->user->id,
'version_key' => $version_key,
'version_val' => $version_val
]);
writeLog($log, 'update_menu_version');
Yii::$app->redis->set($version_key, $version_val);
}

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

  默认的导航高亮是按照模块,控制器,方法来进行直接匹配的,这样一来有一种需求无法满足,比如: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('/' . $actionId)) {
return true;
}
$obj = $action->controller;
do {
if ($user->can('/' . ltrim($obj->getUniqueId() . '/*', '/'))) {
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, '/')) {
return [$url];
}
$url = '/' . trim($url, '/');
$params = explode('/', $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 = '/' . $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;
}

  需要说明的是,注释掉的部分是管理员的权限检查,如果是管理员会自动返回所有的权限,但是这种不太好,因为实际情况中会分多种管理员,这样管理员不一定拥有所有的权限,如果这样不是超级管理员就不能使用,所以用的时候还是要慎重,最好统一使用权限检查。如果感觉那个SQL执行太慢可以添加缓存,缓存过期的时间和菜单过期类似,当用户的权限有变动的时候和菜单修改的时候跟新缓存。两一种解决办法是把这个方法协程单利,利用单利只是执行一次权限的查询,检查的阶段可以单独写成方法提供。

(0)

相关推荐

  • Yii2 rbac权限控制之rule教程详解

    在我们之前Yii2搭建后台并实现rbac权限控制完整实例教程中,不知道你曾经疑惑过没有一个问题,rule表是做什么的,为什么在整个过程中我们都没有涉及到这张表? 相信我不说,部分人也都会去尝试,或百度或google,到头来也会竹篮打水,这部分讲解的内容少之又少啊! 对于一般的权限系统而言,我们之前做的rbac一般情况下是足够的,即时没有rule,相信你也能实现我们用rule实现的功能. 我们就以官网的例子给出一个具体的操作教程,看看这个神秘的rule到底是做什么的! 看需求: 我们有管理员和普通

  • yii2 RBAC使用DbManager实现后台权限判断的方法

    本文实例讲述了yii2 RBAC使用DbManager实现后台权限判断的方法.分享给大家供大家参考,具体如下: 首先根据文档生成yii2 框架中的表 yii migrate --migrationPath=@yii/rbac/migrations/ 生成如下4表: auth_assignment auth_item_child auth_item auth_rule 使用yii的gii快速生成对应的model,但是由于auth_item表同时存储角色跟权限,由于后面我们要分角色跟权限来做curd

  • 深入解析yii权限分级式访问控制的实现(非RBAC法)

    yii framework 提供了2套权限访问系统,一套是简单的filter(过滤器)模式,另一套是复杂全面的RBAC模式,我这里要讲的是第一套(因为我也刚刚学到这里).如 果你有研究过YII官方的demo blog,一定知道,比如,由gii自动生成的user模块,自动附带了简单的filter权限分配功能,具体细节请参照blog手册的"用户验证"一章 节,以及yii官方指南的"验证和授权"一章节.(注意,我这里所指的模块,只是我个人对与user有关的文件的统称,与y

  • yii权限控制的方法(三种方法)

    本文实例讲述了yii权限控制的方法.分享给大家供大家参考,具体如下: 这里摘录以下3种: 1. 通过accessControl: public function filters() { return array( 'accessControl', // perform access control for CRUD operations ); } /** * Specifies the access control rules. * This method is used by the 'acc

  • Yii2 rbac权限控制操作步骤实例教程

    本篇的主题是 rbac权限控制的详细操作步骤,注意是操作步骤哦,关于配置与rbac的搭建,我们在yii2搭建完美后台并实现rbac权限控制实例教程说的再清楚不过了. 但是,在很多人的反馈下,说是完全按照本主的步骤来的,丝毫不差,为啥菜单不显示,为啥不显示呢不显示?艾玛,最终在群里帮人调试的时候找到原因了,si分的不容易啊.原因就是你学会了搭建没学会操作啊,啊,啊,我们今天就来详细的说说这rbac的权限控制具体怎么操作!当然,大前提下是你已经按照我们上文成功搭建一套完美的后台并实现了rbac权限控

  • Yii2搭建后台并实现rbac权限控制完整实例教程

    1.安装yii2 未安装的请参考yii2史上最简单式安装教程,没有之一 已安装的请继续看下一步操作 2.配置数据库 2.1 配置数据库 修改common/config/main-local.php 实际项目中本地的数据库往往跟线上数据库不一致, 我们这里配置到main-local.php就可以了,产品上线后,我们可以使用git或者svn忽略掉main-local.php,线上直接部署. 我们这里使用的mysql数据库,配置如下 当然啦,上面红圈圈的信息需要你自己手动修改掉,要是十分巧合跟我的一样

  • 浅析Yii中使用RBAC的完全指南(用户角色权限控制)

    写在前面    * 我的feed地址已经修改为: http://feeds.imdong.net ,请更新您的阅读器.    * 以下内容适合Yii 1.0.x,其他版本可能有略微的差别.    * 根据您的评论和反馈,本文会不断进行修改和补充,以方便新学习者.开始准备Yii提供了强大的配置机制和很多现成的类库.在Yii中使用RBAC是很简单的,完全不需要再写RBAC代码.所以准备工作就是,打开编辑器,跟我来.设置参数.建立数据库在配置数组中,增加以下内容: 复制代码 代码如下: 'compon

  • 深入浅析Yii admin的权限控制

    说到CMS,最需要有的东西就是权限控制,特别是一些复杂的场景,多用户,多角色,多部门,子父级查看等等.最近在开发一个线下销售的东东,这个系统分为管理员端,省代端,客户端,门店端,销售端, 部门端,部门老大下面分子部门等等,恶心的需求.我们这个项目使用yii框架开发,yii在php届还是比较流行的,虽然说laravel现在横行,但是一些部门一些团队还是采用了yii框架,比如我们. 我是刚接触yii这个框架,开始的时候对这种面向组件的框架甚是别扭.当时打算自己写权限的,自己创建权限表,关联表等,但是

  • Yii2 rbac权限控制之菜单menu实例教程

    在上篇文章给大家介绍了yii2搭建完美后台并实现rbac权限控制实例教程中完美实现了yii2的后台搭建和rbac权限控制,如果你还没有实现,请先看上文再回来参考本文,因为本文是在上文的基础上进行完善和补充. 部分小伙们纷纷反映,最后菜单menu怎么控制权限呀,看不懂,搞不定,而且你那貌似没搞完,瞎忽悠!确实没那么全,今天看我们如何实现菜单完美权限化.先罗列下主要讲的内容,不需要的没必要看下去,只为分享给有需要的人. 利用menu表添加菜单 左侧菜单结果adminlte完美呈现 菜单前面自定义ic

  • YII2框架中使用RBAC对模块,控制器,方法的权限控制及规则的使用示例

    本文实例讲述了YII2框架中使用RBAC对模块,控制器,方法的权限控制及规则的使用.分享给大家供大家参考,具体如下: 在使用YII2中自带的RBAC时,需要先配置config/web.php: return [ // ... 'components' => [ 'authManager' => [ 'class' => 'yii\rbac\DbManager', ], // ... ], ]; 如果你需要运行yii migrate来创建表,那么config/console.php也需要同

  • ASP.NET对HTML页面元素进行权限控制(二)

    这是这个权限控制的第一步,扫描界面把要分配权限的元素的信息获取出来存入到数据库中. 这一步分三小步: (1).标出界面所要分配权限的元素 (2).扫描界面获取所要分配权限的元素信息.(ID,标题,层级关系) (3).存入数据库中. 1.标出界面所要分配权限的元素. 在扫描的时候一开始我觉得很难因为HTML元素过多又有很多层级关系.一开始用的是<div>标签来表示HTML所要分配权限的元素,发现这个方案不行,比如把添加用户按钮加上DIV那么这个按钮的样式就变了还得调样式我现在做的KS系统有将近1

  • spring boot 1.5.4 集成shiro+cas,实现单点登录和权限控制

    1.添加maven依赖(先安装好cas-server-3.5.2,安装步骤请查看本文参考文章) <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.2.4</version> </dependency> <dependency> <groupId>

  • java中自定义Spring Security权限控制管理示例(实战篇)

    背景描述 项目中需要做细粒的权限控制,细微至url + httpmethod (满足restful,例如: https://.../xxx/users/1, 某些角色只能查看(HTTP GET), 而无权进行增改删(POST, PUT, DELETE)). 表设计 为避嫌,只列出要用到的关键字段,其余敬请自行脑补. 1.admin_user 管理员用户表, 关键字段( id, role_id ). 2.t_role 角色表, 关键字段( id, privilege_id ). 3.t_privi

  • javaweb设计中filter粗粒度权限控制代码示例

    1 说明 我们给出三个页面:index.jsp.user.jsp.admin.jsp. index.jsp:谁都可以访问,没有限制: user.jsp:只有登录用户才能访问: admin.jsp:只有管理员才能访问. 2 分析 设计User类:username.password.grade,其中grade表示用户等级,1表示普通用户,2表示管理员用户. 当用户登录成功后,把user保存到session中. 创建LoginFilter,它有两种过滤方式: 如果访问的是user.jsp,查看sess

随机推荐