概述
RBAC, 基于角色的访问控制, 对RBAC有问题的朋友, 可以转移到: 基于角色的访问控制RBAC 一文, 进行了解.
Yii2中, 实现的是NIST RBAC模型.
Yii2中, 的RBAC管理, 涉及到的内容有: 权限, 角色, 规则, 为角色赋予权限, 为用户分配角色.
我们会以一个案例, 贯穿整个RBAC的使用, 请继续阅读, 和跟随练习, 保证可以全面了解Yii2的RBAC机制实现.
<!–more Keep reading this post–>
目标案例准备
准备一: 控制器中的几个动作方法, 完成典型的CRUD方法, 不去展示具体的方法实现, 仅针对访问这些方法的权限进行测试, 代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
<?php
/**
* Created by 小韩说理
* User: 韩忠康
* Date: 2016/10/30
* Time: 21:26
*/
namespace
backend
\
controllers
;
use
yii
\
web
\
Controller
;
class
PostController
extends
Controller
{
public
function
actionAdd
(
)
{
return
$this
->
renderContent
(
'添加'
)
;
}
public
function
actionDelete
(
)
{
return
$this
->
renderContent
(
'删除'
)
;
}
public
function
actionUpdate
(
)
{
return
$this
->
renderContent
(
'更新'
)
;
}
public
function
actionSelect
(
)
{
return
$this
->
renderContent
(
'查询'
)
;
}
}
|
准备二: 创建几个用户, 便于分配不同的角色, 进行访问权限测试. 用户表, 采用yii2提供的默认user表结构, 如果没有可以使用如下的命令创建, 进入yii2项目根目录, 键入下面的命令:
1
2
|
$
.
/
yii
migrate
/
create
create_users
_table
$
.
/
yii
migrate
|
此时用户user表, 就被自动创建出来了. (PS: 你要先配置好数据库连接db组件才可以) 插入几条用户测试数据, 可以用yii2自带的注册功能完成(r=site/signup), 注册完成后, 当前的user表内容和结构如下:
有了以上的动作和用户的基本数据后, 就可以完成, 哪个用户属于某个角色, 是否具有操作某个动作的RBAC测试了. 继续..
配置授权管理组件
授权管理组件authManager, 是完成Yii2中权限管理和检查的核心组件, 因此需要先配置. 以Php文件存储授权数据为例, config/web.php配置如下:
1
2
3
4
5
6
|
return
[
'components'
=
>
[
'authManager'
=
>
[
'class'
=
>
'yii\rbac\PhpManager'
,
]
,
}
|
该授权组件, 可选择由PHP文件存储授权数据或使用数据库存储授权数据. 分别使用yii\rbac\PhpManager和yii\rbac\DbManager实现. 文件存储直观, 编辑简单, 但不适合频繁大量的授权更新操作. 而数据库管理方案适合频繁的授权变动. 请依据自己项目的情况自行选择. 以上的配置中, 选择的PhpManager, 以PHP文件方式存储授权, 此时需要@app/rbac目录(backend/rbac)存在, 并具有可写权限. 如果需要使用数据库管理授权数据, 请看后边的DbManager授权管理小节.
配置完毕后, 使用\Yii::$app->authManager就可以访问到授权组件了
管理角色
角色是yii\rbac\Role类的实例, 通过\Yii::$app->authManage组件的createRole()方法可以创建, 创建角色时需要为角色确定ID. 创建完毕后, 使用\Yii::$app->authManage组件的add()方法将角色添加. 示例代码如下:
1
2
3
4
5
6
7
8
9
|
// 获取authMananger组件对象
$auth
=
\
Yii::
$app
->
authManager
;
// 增加3个角色
$author
=
$auth
->
createRole
(
'author'
)
;
$auth
->
add
(
$author
)
;
$reader
=
$auth
->
createRole
(
'reader'
)
;
$auth
->
add
(
$reader
)
;
$editor
=
$auth
->
createRole
(
'editor'
)
;
$auth
->
add
(
$editor
)
;
|
可以将以上代码定义在任意的控制器方法中, 请求运行该方法, 就会生成3个角色, 存储在PHP文件或者数据表中.取决于实现authManager组件的具体类. 我们选择的PhpManager, 则会存储在文件中.
文件位于@app/rbac目录的items.php文件中:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
items
.
php
<?php
return
[
'author'
=
>
[
'type'
=
>
1
,
]
,
'reader'
=
>
[
'type'
=
>
1
,
]
,
'editor'
=
>
[
'type'
=
>
1
,
]
,
]
;
|
可见, 增加了author, reader, editor的3个角色.
额外的, 角色不但可以增加, 还可以删除, 使用authManager对象的方法remove(), 就可以删除:
1
2
|
$editor
=
$auth
->
createRole
(
'editor'
)
;
$auth
->
remove
(
$editor
)
;
|
为用户分配角色
有了角色之后, 就可以将用户和角色关联起来了, 去设置, 那个用户属于那个角色.
使用\Yii::$app->authManager组件对象的assign()方法, 就可以为用户分配角色. 或者将角色分配给某个用户.
使用下面的代码, 可以为我们之间的测试用户分配不同的角色:
1
2
3
4
5
6
7
|
// 为用户分配角色
$auth
->
assign
(
$author
,
2
)
;
$auth
->
assign
(
$reader
,
2
)
;
$auth
->
assign
(
$reader
,
3
)
;
$auth
->
assign
(
$reader
,
4
)
;
$auth
->
assign
(
$editor
,
5
)
;
// 2, 3, 4, 5是不同的用户ID.
|
可以将以上代码定义在任意的控制器方法中, 请求运行该方法. 就分配了不同的角色给不同的用户. 其中2ID的用户, 同时属于两个角色 author和reader.
同样的, 分配完成后, \Yii::$app->authManager组件对象会记录下用户所属的角色, 记录在@app/rbac/assignments.php文件中, 本例的结果如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
<
?
php
return
[
2
=
>
[
'author'
,
'reader'
,
]
,
3
=
>
[
'reader'
,
]
,
4
=
>
[
'reader'
,
]
,
5
=
>
[
'editor'
,
]
,
]
;
|
可以看到, 不同的用户ID, 对应不同的角色. 而且2ID用户对于2个角色, author和reader.
到此, 为用户分配角色的任务完成.
ACF权限过滤(静态的授权管理)
当用户与角色的关系建立完毕后, 就可以去配置授权了.
配置方法之一, 也是比较容易的方法, 就是使用之前说过的ACF访问控制过滤器(ACF不了解的朋友, 可以参考小韩说理的: Yii2权限管理之ACF), 完成哪个角色的用户可以访问那个动作的过滤, 示例过滤器的配置如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
@
app
/
controllers
/
PostController
.
php
class
PostController
extends
Controller
{
// 定义过滤器的方法
public
function
behaviors
(
)
{
return
[
// 访问过滤控制器
'access'
=
>
[
'class'
=
>
AccessControl
::
className
(
)
,
'only'
=
>
[
'add'
,
'delete'
,
'update'
,
'select'
]
,
'rules'
=
>
[
// author角色的访问授权
[
'allow'
=
>
true
,
'actions'
=
>
[
'add'
,
'update'
]
,
'roles'
=
>
[
'author'
]
,
]
,
// editor角色的访问授权
[
'allow'
=
>
true
,
'actions'
=
>
[
'update'
,
'select'
,
'delete'
]
,
'roles'
=
>
[
'editor'
]
,
]
,
// reader的访问授权
[
'allow'
=
>
true
,
'actions'
=
>
[
'select'
]
,
'roles'
=
>
[
'reader'
]
,
]
,
]
,
]
,
]
;
}
// 此控制器的其他代码, 包含: add, select, update, delete动作
}
|
经过以上的配置之后, 就确定了, 哪些角色可以访问哪些动作. 这样, 当特定的用户登录时, 确定当前用户的角色, 就可以确定是否有权限访问动作了.
测试访问
直接晒结果:
用户-ID-角色 | 动作add | 动作delete | 动作update | 动作select |
xiao-2-author-reader | yes | no | yes | yes |
han-3-reader | no | no | no | yes |
shuo-4-reader | no | no | no | yes |
li-5-editor | yes | yes | yes | no |
可见, 配置好之后, 就可以依据我们的规则, 规范用户的访问授权了.
其实, 到此, 就已经完成了, RBAC, 基于角色的访问控制的实现. 可以好好整理, 休息下了.
实操中, 新的问题就是, 如何动态的配置权限? 如何更精致的管理权限? ACF的方式就有点吃力的, 因为他需要与定义在控制器中的behaviors()方法配合使用, 如果需要更改权限, 就需要更改behaviors()中的代码. 因此, 想要获得更动态的授权的管理办法, 请继续阅读: 授权管理和规则管理 小节, 获得更完整的内容.
authManager授权管理(动态的授权管理)
动态管理权限, 其实就是将用权限分配和校验, 都是使用PHP代码完成, 在程序运行时完成, 需要的方法如下
$auth = \Yii::$app->authMananger;
$auth->createPermission(), 创建权限对象
$auth->createRole(), 创建角色对象
$auth->add(), 添加角色或者权限或者规则
$auth->addChild(), 为角色赋予权限, 添加子权限
$auth->assign(), 为用户分配角色
$auth->remove(), 删除某个授权数据
$auth->removeAll(), 删除全部授权数据包括, 角色, 权限, 规则
\Yii::$app->user->can(‘权限’))检测用户是否具有某权限
以上方法, 有的已经用过了.
下面就组成一个可以动态管理的代码示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
// 增加的用于测试权限管理方法的动作, 放在控制器中请求执行即可
public
function
actionAuth
(
)
{
$auth
=
\
Yii::
$app
->
authManager
;
// 删除全部
$auth
->
removeAll
(
)
;
// 增加权限
$postAdd
=
$auth
->
createPermission
(
'postAdd'
)
;
$postAdd
->
description
=
'文章添加'
;
$auth
->
add
(
$postAdd
)
;
$postDelete
=
$auth
->
createPermission
(
'postDelete'
)
;
$postDelete
->
description
=
'文章删除'
;
$auth
->
add
(
$postDelete
)
;
$postUpdate
=
$auth
->
createPermission
(
'postUpdate'
)
;
$postUpdate
->
description
=
'文章编辑'
;
$auth
->
add
(
$postUpdate
)
;
$postSelect
=
$auth
->
createPermission
(
'postSelect'
)
;
$postSelect
->
description
=
'文章查看'
;
$auth
->
add
(
$postSelect
)
;
// 增加角色
$author
=
$auth
->
createRole
(
'author'
)
;
$auth
->
add
(
$author
)
;
$reader
=
$auth
->
createRole
(
'reader'
)
;
$auth
->
add
(
$reader
)
;
$editor
=
$auth
->
createRole
(
'editor'
)
;
$auth
->
add
(
$editor
)
;
// 为角色赋予权限
$auth
->
addChild
(
$author
,
$postAdd
)
;
// 作者 添加文章
$auth
->
addChild
(
$author
,
$postUpdate
)
;
// 作者 编辑文章
$auth
->
addChild
(
$reader
,
$postSelect
)
;
// 读者 看文章
$auth
->
addChild
(
$editor
,
$postDelete
)
;
$auth
->
addChild
(
$editor
,
$postSelect
)
;
$auth
->
addChild
(
$editor
,
$author
)
;
$auth
->
addChild
(
$editor
,
$reader
)
;
// 为用户分配角色
$auth
->
assign
(
$author
,
2
)
;
$auth
->
assign
(
$reader
,
2
)
;
$auth
->
assign
(
$reader
,
3
)
;
$auth
->
assign
(
$reader
,
4
)
;
$auth
->
assign
(
$editor
,
5
)
;
}
|
以上代码一旦运行, 就会生成权限文件和权限分配文件如下:
权限文件:items.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
items
.
php权限列表文件
<
?
php
return
[
'postAdd'
=
>
[
'type'
=
>
2
,
'description'
=
>
'文章添加'
,
]
,
'postDelete'
=
>
[
'type'
=
>
2
,
'description'
=
>
'文章删除'
,
]
,
'postUpdate'
=
>
[
'type'
=
>
2
,
'description'
=
>
'文章编辑'
,
]
,
'postSelect'
=
>
[
'type'
=
>
2
,
'description'
=
>
'文章查看'
,
]
,
'author'
=
>
[
'type'
=
>
1
,
'children'
=
>
[
'postAdd'
,
'postUpdate'
,
]
,
]
,
'reader'
=
>
[
'type'
=
>
1
,
'children'
=
>
[
'postSelect'
,
]
,
]
,
'editor'
=
>
[
'type'
=
>
1
,
'children'
=
>
[
'postDelete'
,
'postSelect'
,
'author'
,
'reader'
,
]
,
]
,
]
;
|
角色分配文件:assignments.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
assignments
.
php角色分配文件
<?php
return
[
2
=
>
[
'author'
,
'reader'
,
]
,
3
=
>
[
'reader'
,
]
,
4
=
>
[
'reader'
,
]
,
5
=
>
[
'editor'
,
]
,
]
;
|
大家可以看到, 这个角色分配文件, 与我们之前的一致.
在此基础上, 增加了权限记录文件items.php 这个文件记录下来了那个角色有哪些权限, 这样就不需要再使用ACF进行权限与动作的对应了.
注意, 在yii的RBAC系统中, 可以将角色视为权限的集合, 因此 才会有代码:
1
2
3
4
5
6
7
|
$
auth
->
addChild
(
$
author
,
$
postAdd
)
;
// 作者 添加文章
$
auth
->
addChild
(
$
author
,
$
postUpdate
)
;
// 作者 编辑文章
$
auth
->
addChild
(
$
reader
,
$
postSelect
)
;
// 读者 看文章
$
auth
->
addChild
(
$
editor
,
$
postDelete
)
;
$
auth
->
addChild
(
$
editor
,
$
postSelect
)
;
$
auth
->
addChild
(
$
editor
,
$
author
)
;
// 添加author角色权限为editor权限的子权限
$
auth
->
addChild
(
$
editor
,
$
reader
)
;
|
将权限设置为角色的子对象, 和将角色设置为角色的自对象. 可以理解成权限的集合, 和集合的合并(继承)操作.
有了以上的代码, 就有能力去动态的设置角色的权限了. 可以在后台增加一个权限管理界面, 完成角色的权限分配了.
权限的校验(动态校验)
当使用了动态授权管理后, 通常就不去使用ACF管理权限了.
就可以使用user组件提供的can()方法来检测当前用户是否具有某个权限了. 在某个具体的动作中, 使用方法: $user->can(‘postAdd’) 就可以知道是否具有操作postAdd的权限, 示例代码如下:
1
2
3
4
5
6
7
8
9
|
public
function
actionAdd
(
)
{
$user
=
\
Yii::
$app
->
user
;
if
(
!
$user
->
can
(
'postAdd'
)
)
{
return
$this
->
renderContent
(
'没有权限执行该操作'
)
;
}
return
$this
->
renderContent
(
'添加'
)
;
}
|
此时, 登录的用户, 就可以判断是否具有postAdd权限, 从而去继续执行. 此时不再需要配置access过滤器了.
有了该方法, 就可以完全自由的去选择, 在何处完成校验权限了.
(PS: 也可以使用yii\rbac\ManagerInterface::checkAccess()方法)
自定义授权规则
我们除了可以动态的管理授权规则外, Yii中还提供更加自由的自定义的授权规则, 例如, 我们所定义的例子, 作者角色不能删除post, 包括他自己的. 此时, 在业务逻辑上就会出现问题.
如果需要修正, 作者不能删除post, 但是可以删除自己发布的post这个逻辑上权限问题, 就可以使用自定义授权规则rule完成:
我们需要先创建一个规则, 起个名字, 并在execute()方法中定义自己的验证逻辑, 示例代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
<?php
/**
* Created by 小韩说理
* User: 韩忠康
* Date: 2016/11/2
* Time: 0:03
*/
namespace
backend
\
rbac
;
use
yii
\
rbac
\
Rule
;
class
AuthorDelete
extends
Rule
{
public
$name
=
'deleteIsAuthor'
;
/**
* @param string|integer $user 用户 ID.
* @param Item $item 该规则相关的角色或者权限
* @param array $params 传给 ManagerInterface::checkAccess() 的参数
* @return boolean 代表该规则相关的角色或者权限是否被允许
*/
public
function
execute
(
$user
,
$item
,
$params
)
{
// 判断post的createdby是否为当前用户($user为当前用户ID)
return
isset
(
$params
[
'post'
]
)
?
$params
[
'post'
]
->
createdBy
==
$user
:
false
;
}
}
|
定义好规则后, 与某个权限绑定, 之后将权限与作者author角色绑定:
增加规则, 增加绑定规则的权限, 权限分配角色, 设置权限层级:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
// 增加规则
$
authorDeleteRule
=
new
\
backend
\
rbac
\
AuthorDelete
;
$
auth
->
add
(
$
authorDeleteRule
)
;
// 增加权限, 绑定规则
$
authorDelete
=
$
auth
->
createPermission
(
'authorDelete'
)
;
$
authorDelete
->
description
=
'作者删除文章允许'
;
// 绑定规则
$
authorDelete
->
ruleName
=
$
authorDeleteRule
->
name
;
$
auth
->
add
(
$
authorDelete
)
;
// 将权限分配给author角色
$
auth
->
addChild
(
$
author
,
$
authorDelete
)
;
// postDelete规则作为authorDelete规则的子规则, 这样, 在检测postDelete规则时就会向上找到authorDelete规则
$
auth
->
addChild
(
$
authorDelete
,
$
postDelete
)
;
|
设置完毕后, 在@app/rbac/rules.php文件中, 就生成了规则的配置内容:
1
2
3
4
5
|
rules
.
php
<
?
php
return
[
'deleteIsAuthor'
=
>
'O:25:"backend\\rbac\\AuthorDelete":3:{s:4:"name";s:14:"deleteIsAuthor";s:9:"createdAt";N;s:9:"updatedAt";N;}'
,
]
;
|
规则配置好后, 在检测用户权限时, 传递额外的post参数表示文章, 即可:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
// 测试用删除动作
public
function
actionDelete
(
)
{
// 伪造的文章
$post
=
new
\
stdClass
(
)
;
// 文章作者ID
$post
->
createdBy
=
2
;
// xiao的ID, yes
// $post->createdBy = 3;// han的ID, no
$user
=
\
Yii::
$app
->
user
;
// 校验时, 传递当前文章
if
(
!
$user
->
can
(
'postDelete'
,
[
'post'
=
>
$post
]
)
)
{
return
$this
->
renderContent
(
'没有权限执行该操作'
)
;
}
return
$this
->
renderContent
(
'删除'
)
;
}
|
上面的测试中, 假设文章作者的ID是2, 这样, 在xiao用户, 登录时, xiao用户没有postDelete权限, 但是执行该操作是可以成功的! 就是执行了我们定义的规则校验!
规则, 可大大提升rbac的授权验证法则, 不仅仅限于动作层面, 可以增加自己的业务逻辑, 一下就使得权限的验证的规则就上升了一个层次. 值得大家去好好好好学习.
.