使用RBAC

基于角色的访问控制(RBAC)提供了简单但是非常强大的中心化访问控制。它是Yii中最强大的访问控制方法。在指导中有关于它的描述,但因为比较复杂和强大,如果不了解一些底层原理的话,比较难以理解。

在本小节中,我们将会take the roles hierarchy from the definitive guide, import it, and explain what is happening internally.

准备

  1. 按照官方指南http://www.yiiframework.com/doc-2.0/guide-start-installation.html的描述,使用Composer包管理器创建一个新的应用。
  2. 创建一个MySQL数据库并配置。
  3. config/main.phpconfig/console.php中配置authManager组件:
  1. return [
  2. // ...
  3. 'components' => [
  4. 'authManager' => [
  5. 'class' => 'yii\rbac\DbManager',
  6. ],
  7. // ...
  8. ],
  9. ];
  1. 运行migration:
  1. yii migrate --migrationPath=@yii/rbac/migrations

如何做…

执行如下步骤:

  1. 创建访问规则rbac/AuthorRule.php
  1. <?php
  2. namespace app\rbac;
  3. use yii\rbac\Rule;
  4. /**
  5. * Class AuthorRule.
  6. * @package app\rbac
  7. */
  8. class AuthorRule extends Rule
  9. {
  10. public $name = 'isAuthor';
  11. /**
  12. * @param int|string $user
  13. * @param \yii\rbac\Item $item
  14. * @param array $params
  15. *
  16. * @return bool
  17. */
  18. public function execute($user, $item, $params)
  19. {
  20. return isset($params['post']) ?
  21. $params['post']->createdBy == $user : false;
  22. }
  23. }
  1. 创建一个控制台命令command/RbacController.phpinitRBAC规则命令:
  1. <?php
  2. namespace app\commands;
  3. use app\models\User;
  4. use Yii;
  5. use yii\console\Controller;
  6. /**
  7. * Class RbacController.
  8. * @package app\commands
  9. */
  10. class RbacController extends Controller
  11. {
  12. public function actionInit()
  13. {
  14. $auth = Yii::$app->authManager;
  15. $createPost = $auth->createPermission('createPost');
  16. $createPost->description = 'Create a post';
  17. $updatePost = $auth->createPermission('updatePost');
  18. $updatePost->description = 'Update a post';
  19. $updatePost = $auth->createPermission('updatePost');
  20. $updatePost->description = 'Update a post';
  21. $deletePost = $auth->createPermission('deletePost');
  22. $deletePost->description = 'Delete a post';
  23. $readPost = $auth->createPermission('readPost');
  24. $readPost->description = 'Read a post';
  25. $authorRule = new \app\rbac\AuthorRule();
  26. // add permissions
  27. $auth->add($createPost);
  28. $auth->add($updatePost);
  29. $auth->add($deletePost);
  30. $auth->add($readPost);
  31. $auth->add($authorRule);
  32. // add the "updateOwnPost" permission and associate the rule with it.
  33. $updateOwnPost = $auth->createPermission('updateOwnPost');
  34. $updateOwnPost->description = 'Update own post';
  35. $updateOwnPost->ruleName = $authorRule->name;
  36. $auth->add($updateOwnPost);
  37. $auth->addChild($updateOwnPost, $updatePost);
  38. // create Author role
  39. $author = $auth->createRole('author');
  40. $auth->add($author);
  41. $auth->addChild($author, $createPost);
  42. $auth->addChild($author, $updateOwnPost);
  43. $auth->addChild($author, $readPost);
  44. // create Admin role
  45. $admin = $auth->createRole('admin');
  46. $auth->add($admin);
  47. $auth->addChild($admin, $updatePost);
  48. $auth->addChild($admin, $deletePost);
  49. $auth->addChild($admin, $author);
  50. // assign roles
  51. $auth->assign($admin, User::findByUsername('admin')->id);
  52. $auth->assign($author, User::findByUsername('demo')->id);
  53. echo "Done!\n";
  54. }
  55. }
  1. 在控制台中运行:
  1. yii rbac/init
  1. 创建controllers/RbacController.php
  1. <?php
  2. namespace app\controllers;
  3. use app\models\User;
  4. use stdClass;
  5. use Yii;
  6. use yii\filters\AccessControl;
  7. use yii\helpers\Html;
  8. use yii\web\Controller;
  9. /**
  10. * Class RbacController.
  11. */
  12. class RbacController extends Controller
  13. {
  14. public function behaviors()
  15. {
  16. return [
  17. 'access' => [
  18. 'class' => AccessControl::className(),
  19. 'rules' => [
  20. [
  21. 'allow' => true,
  22. 'actions' => ['delete'],
  23. 'roles' => ['deletePost'],
  24. ],
  25. [
  26. 'allow' => true,
  27. 'actions' => ['test'],
  28. ],
  29. ],
  30. ],
  31. ];
  32. }
  33. public function actionDelete()
  34. {
  35. return $this->renderContent(
  36. Html::tag('h1', 'Post deleted.')
  37. );
  38. }
  39. /**
  40. * @param $description
  41. * @param $rule
  42. * @param array $params
  43. *
  44. * @return string
  45. */
  46. protected function renderAccess($description, $rule, $params = [])
  47. {
  48. $access = Yii::$app->user->can($rule, $params);
  49. return $description.': '.($access ? 'yes' : 'no');
  50. }
  51. public function actionTest()
  52. {
  53. $post = new stdClass();
  54. $post->createdBy = User::findByUsername('demo')->id;
  55. return $this->renderContent(
  56. Html::tag('h1', 'Current permissions').
  57. Html::ul([
  58. $this->renderAccess('Use can create post',
  59. 'createPost'),
  60. $this->renderAccess('Use can read post',
  61. 'readPost'),
  62. $this->renderAccess('Use can update post',
  63. 'updatePost'),
  64. $this->renderAccess('Use can own update post',
  65. 'updateOwnPost', [
  66. 'post' => $post,
  67. ]),
  68. $this->renderAccess('Use can delete post',
  69. 'deletePost'),
  70. ])
  71. );
  72. }
  73. }
  1. 运行一次rbac/test检查access to all the created permissions of the RBAC hierachy:

使用RBAC - 图1

  1. 然后尝试使用demo登录(密码是demo),再次运行rbac/test

使用RBAC - 图2

  1. 然后尝试使用admin登录(密码是admin),再次运行rbac/test

使用RBAC - 图3

  1. demo用户登录,运行rbac/delete

使用RBAC - 图4

  1. admin用户登录,运行rbac/delete

使用RBAC - 图5

工作原理…

Yii模仿NIST RBAC模型实现了一个一般的层次化的RBAC。它通过应用组件authManager提供了RBAC功能。

RBAC层级是一个有向无环图,也就是说,它由结点和有向连接边组成。有三种类型的结点:角色、权限和规则。

角色是权限(例如创建帖子和更新帖子)的集合。一个角色可以分配给一个或多个用户。为了检查用户是否有某个指定的权限,我们可以检查这个用户是否被赋予了拥有该权限的角色。

角色和权限都可以以等级化的方式组织。特别地,一个角色可以包含其它角色和权限,并且权限可以包含其它权限。Yii实现了一个偏序层级,它包含了特定的tree等级。当一个角色包含一个权限时,反过来说是不正确的。

为了测试权限,我们创建了两个动作。第一个动作是test,包含了创建权限和角色的检查器。第二个动作是delete,它被访问过滤器限制了访问。访问过滤的规则如下所示:

  1. [
  2. 'allow' => true,
  3. 'actions' => ['delete'],
  4. 'roles' => ['deletePost'],
  5. ],

这意味着,我们允许所有拥有deletePost权限的用户运行deletePost动作。Yii以检查deletePost权限开始。注意到访问规则元素被命名为roles,你可以指定一个RBAC等级节点,无论是角色、规则还是权限。检查updatePost是复杂的:

  1. Yii::$app->user->can('updatePost', ['post' => $post]);

我们使用第二个参数来传递一个帖子(在我们的例子中,我们使用stdClass来模拟它)。如果用户以demo登录,然后获得了updatePost的权限。如果你很幸运,你只需要go through updatePostupdateOwnPost和作者。

因为updateOwnPost有一个定义好的规则,它会在传参给checkAccess时运行。如果结果为真,访问将会得到授权。因为Yii不知道最短的方法是什么,它会尝试检查所有可能性直至成功,或者没有剩余的备选项。

更多…

下面是一些有用的技巧,能让你更方便的使用RBAC。

保持层级简单和高效

遵守如下建议来提升性能,并降低层级复杂性:

  • 避免给一个用户关联多个角色
  • 不要连接相同类型的结点:例如,避免连接两个task

命名RBAC结点

一个复杂的层级如果不使用一些命名习惯的话会很难理解。能帮助我们降低复杂性的惯例是:

  1. [group_][own_]entity_action

只有当当前用户是元素的拥有者时,才能修改这个元素的能力。这是,会使用own这个关键词。group只是一个命名空间。entity是我们工作的实体名称,action是我们执行的动作。

例如,如果我们需要创建一个规则,它决定了用户是否可以删除一个博客文章,我们把它命名为blog_post_delete。如果这个规则决定了用户是否可以编辑他自己的评论,我们将会把它命名为blog_own_comment_edit

参考

为了了解更多关于SQL注入和使用Yii处理数据库,参考如下链接: