上面两个章节,我们主要介绍了什么是RBAC。今天我们准备用手去描述下这个操作,想想还是有点激动的。
我们知道,在一般的管理平台上都需要给用户分配权限,除非是要求非常简陋的需求,正好你也不会,所以就糊弄过去了。
为了讲解的更真实,今天我们就把博客相关的一系列操作权限(增删改查等)分配给用户。终于可以敲代码了,憋屈了两节的枯燥一扫而去。
1、首先,我们创建一张blog表,用于后面的操作。1
2
3
4
5
6
7
8
9
10
11DROP TABLE IF EXISTS `blog`;
CREATE TABLE `blog` (
`id` int(11) NOT NULL,
`title` varchar(100) NOT NULL DEFAULT '' COMMENT '标题',
`content` text NOT NULL COMMENT '内容',
`views` int(11) NOT NULL DEFAULT '0' COMMENT '点击量',
`is_delete` tinyint(4) NOT NULL DEFAULT '1' COMMENT '是否删除 1未删除 2已删除',
`created_at` datetime NOT NULL COMMENT '添加时间',
`updated_at` datetime NOT NULL COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2、gii生成模板,不会的请参考这篇文章
现在我们通过 /index.php?r=blog 可以看到博客的一系列操作。此时,博客相关的一系列操作,是游离在 RBAC 和 ACF 之外的,也就是任何人都可以访问的。
接下来就要上演重点的关键时刻了。我们要想办法通过rbac将其相关的一系列操作分配给用户,只有分配了权限的用户才可以访问!
那有同学要说了,既然这样,我可以直接把路由跟用户进行绑定吗,用户访问的时候我直接查一下二者的关联,有就让访问,没有就不让访问。
可以呀,但是这样就跟我们的RBAC毫无关系啦。当然,重点是,假设每个人有100甚至更多的操作,你是不是每个人都一个一个的去绑定呢?再者,假设我们新开发了一个功能,要求这后台的80%的人员都有权限访问,你怎么操作呢?每个人再分配一次?那你这天天加班到很晚可就不怪我咯!
再说明白点,我们今天就是要利用 auth_assignment、auth_item 和 auth_item_child 表来实现rbac的授权!其实还有一张 auth_rule 表,我们暂时先不考虑。
那我们就来实现吧。但是但是,这其实大部分的授权接口,官方其实都已经为我们实现好了!我们只需要去操作官方实现好的接口即可。
为了练习,我们手动创建一个控制器 RbacController 来做这些操作。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22<?php
namespace backend\controllers;
use Yii;
use yii\web\Controller;
class RbacController extends Controller
{
public function actionInit ()
{
// 这个是我们上节课添加的authManager组件,组件的调用方式没忘吧?
$auth = Yii::$app->authManager;
// 添加 "/blog/index" 权限
$blogIndex = $auth->createPermission('/blog/index');
$blogIndex->description = '博客列表';
$auth->add($blogIndex);
// 创建一个角色 '博客管理',并为该角色分配"/blog/index"权限
$blogManage = $auth->createRole('博客管理');
$auth->add($blogManage);
$auth->addChild($blogManage, $blogIndex);
// 为用户 test1(该用户的uid=1) 分配角色 "博客管理" 权限
$auth->assign($blogManage, 1); // 1是test1用户的uid
}
}
现在我们访问 /index.php?r=rbac/init 就意味着我们为用户test1分配了角色 “博客管理”,而博客管理包含着”blog/index”的访问权限。
分配好了以后我们还需要为 blog/index 实现一个校验方法,只有分配了权限的用户才可以访问,否则肯定要给用户403提示了!
授权校验,yii已经实现了,我们只需要调用授权校验方法即可。
BlogController 的 index方法修改如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16/**
* Lists all Blog models.
* @return mixed
*/
public function actionIndex()
{
if (!Yii::$app->user->can('/blog/index')) {
throw new \yii\web\ForbiddenHttpException("没权限访问.");
}
$searchModel = new BlogSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
现在我们在未登录或者不是用用户test1登录的情况下访问 /index.php?r=blog 会发现,还真的提示我们403呢,而用户 test1 则访问正常。
从分配到授权校验,功能我们虽然实现了,但是我们想一下这背后隐藏的问题:
单纯的说,分配的话没有多大问题,因为我们可以把分配单独做一个模块,让管理员自己去给用户分配角色,分配权限。但是,如果每一个action我们都要这样操作,那很多控制器 and 千千万万个操作,这样校验,也不比 ACF 省事嘛?
怎么办?我们假设控制器有10个action,目前来说我们需要在10个action内,写10遍判断,对吧?
其实,在执行action之前,yii2内部会先执行一个叫 beforeAction 的操作。那么也就是说如果我们可以在这个叫 beforeAction 的操作内获取到当前要执行的具体action,那么我们只需要在控制器的beforeAction内判断一次就够了,对不对?
很nice,我们现在已经提供了接近10倍的效率。但是以上都是我们臆想的,其实这都是真的啦,确实可以这么实现。我们不妨来看看。
打开你的 BlogController 增加一个beforeAction,代码如下1
2
3
4
5
6
7
8
9public function beforeAction($action)
{
$currentRequestRoute = $action->getUniqueId();
if (!Yii::$app->user->can('/' . $currentRequestRoute)) {
throw new \yii\web\ForbiddenHttpException("没权限访问.");
}
return true;
}
然后把index action的 Yii::$app->user->can 那段判断隐藏掉,现在试图访问下 index 操作,看看是不是达到了同样的效果啦?
但是啊但是,问题总是那么多,假设我现在已经有了100个控制器,难不成我还要一个一个的打开修改吗?我们在controller的父类的beforeAction去操作?可是还是要改100次呢!有没有一种办法,让一个类直接“注入”到当前控制器,这100个控制器我们看都不用看,写一次判断,全部生效的方法喃?
既然都这么说了,你相信我肯定有办法对不对?这就是我们下一章节要讲的行为,期待吧!