在 Yii2 中,需要判断当前登录用户是否具体接口访问权限(基于 RBAC、默认角色)
1、当前的权限判断,是在控制器方法中实现如下
$conventionId = $this->request->post('convention_id', ''); $convention = Convention::find()->where([ 'id' => $conventionId ])->limit(1)->one(); if (!$convention) { return [ 'code' => 10403, 'message' => '大会不存在!', ]; } $mapping = CompanyEmployeeMapping::find()->where([ 'user_id' => Yii::$app->user->id, 'company_id' => $convention->company_id, ])->limit(1)->one(); if (!$mapping) { //判断是否为联络员 $contact = ConventionContact::find()->where('convention_id = :conventionId AND (user_id = :userId OR phone = :phone)', [ ':conventionId' => $conventionId, ':userId' => Yii::$app->user->id, ':phone' => Yii::$app->user->identity->pure_phone_number, ])->limit(1)->one(); //判断是否为签到管理员 $checkinManager = ConventionCheckinManager::find()->where('convention_id = :conventionId AND (user_id = :userId OR phone = :phone)', [ ':conventionId' => $conventionId, ':userId' => Yii::$app->user->id, ':phone' => Yii::$app->user->identity->pure_phone_number, ])->limit(1)->one(); if (!$contact && !$checkinManager) { return [ 'code' => 10001, 'message' => '您没有数据权限', ]; } }
SELECT * FROM `conventions` WHERE `id`='1826937119386051' LIMIT 1 SELECT * FROM `company_employee_mapping` WHERE (`user_id`='1826937119386043') AND (`company_id`='1826937119386040') LIMIT 1 SELECT * FROM `convention_contacts` WHERE convention_id = '1826937119386051' AND (user_id = '1826937119386043' OR phone = '13980074657') LIMIT 1 SELECT * FROM `convention_checkin_managers` WHERE convention_id = '1826937119386051' AND (user_id = '1826937119386043' OR phone = '13980074657') LIMIT 1
2、计划基于 RBAC 实现,避免在每个控制器方法中重复复制(即使提炼出一个公共的方法,也不够灵活)
3、 创建自定义规则类 在 common/rbac/ 目录下创建以下规则类:
CompanyEmployeeRule.php
<?php namespace common\rbac; use yii\rbac\Item; use yii\rbac\Rule; use common\models\CompanyEmployeeMapping; /** * 检查 user_id 是否和通过参数传进来的 user 参数相符 */ class CompanyEmployeeRule extends Rule { public $name = 'isCompanyEmployee'; /** * @param string|integer $user 用户 ID. * @param Item $item 该规则相关的角色或者权限 * @param array $params 传给 ManagerInterface::checkAccess() 的参数 * @return boolean 代表该规则相关的角色或者权限是否被允许 */ public function execute($user, $item, $params): bool { if (!isset($params['convention'])) { return false; } return CompanyEmployeeMapping::find() ->where([ 'user_id' => $user, 'company_id' => $params['convention']->company_id, ]) ->exists(); } }
ConventionContactRule.php
<?php namespace common\rbac; use common\models\User; use yii\rbac\Item; use yii\rbac\Rule; use common\models\ConventionContact; /** * 检查 user_id 是否和通过参数传进来的 user 参数相符 */ class ConventionContactRule extends Rule { public $name = 'isConventionContact'; /** * @param string|integer $user 用户 ID. * @param Item $item 该规则相关的角色或者权限 * @param array $params 传给 ManagerInterface::checkAccess() 的参数 * @return boolean 代表该规则相关的角色或者权限是否被允许 */ public function execute($user, $item, $params): bool { if (!isset($params['convention'])) { return false; } $userModel = User::findOne($user); return ConventionContact::find() ->where([ 'convention_id' => $params['convention']->id, 'user_id' => $user, 'phone' => $userModel->pure_phone_number, ])->exists(); } }
CheckinManagerRule.php
<?php namespace common\rbac; use common\models\User; use yii\rbac\Item; use yii\rbac\Rule; use common\models\ConventionCheckinManager; /** * 检查 user_id 是否和通过参数传进来的 user 参数相符 */ class CheckinManagerRule extends Rule { public $name = 'isCompanyEmployee'; /** * @param string|integer $user 用户 ID. * @param Item $item 该规则相关的角色或者权限 * @param array $params 传给 ManagerInterface::checkAccess() 的参数 * @return boolean 代表该规则相关的角色或者权限是否被允许 */ public function execute($user, $item, $params): bool { if (!isset($params['convention'])) { return false; } $userModel = User::findOne($user); return ConventionCheckinManager::find() ->where([ 'convention_id' => $params['convention']->id, 'user_id' => $user, 'phone' => $userModel->pure_phone_number, ])->exists(); } }
4、所谓默认角色就是 隐式 地指派给 所有 用户的角色。不需要调用 yii\rbac\ManagerInterface::assign() 方法做显示指派,并且授权数据中不包含指派信息。使用 ./yii migrate/create init_convention_rbac 创建新迁移,然后实现创建层次结构
<?php use yii\db\Migration; use common\rbac\CompanyEmployeeRule; use common\rbac\ConventionContactRule; use common\rbac\CheckinManagerRule; use yii\base\Exception; /** * Class m250331_034538_init_convention_rbac */ class m250331_034538_init_convention_rbac extends Migration { /** * @return void * @throws Exception */ public function safeUp() { $auth = Yii::$app->authManager; // 清除所有现有RBAC数据 $auth->removeAll(); // 创建自定义规则 $companyEmployeeRule = new CompanyEmployeeRule(); $auth->add($companyEmployeeRule); $conventionContactRule = new ConventionContactRule(); $auth->add($conventionContactRule); $checkinManagerRule = new CheckinManagerRule(); $auth->add($checkinManagerRule); // 创建权限 $accessConvention = $auth->createPermission('accessConvention'); $accessConvention->description = '访问大会数据权限'; $auth->add($accessConvention); // 创建角色并分配规则 $companyEmployee = $auth->createRole('companyEmployee'); $companyEmployee->ruleName = $companyEmployeeRule->name; $auth->add($companyEmployee); $auth->addChild($companyEmployee, $accessConvention); $conventionContact = $auth->createRole('conventionContact'); $conventionContact->ruleName = $conventionContactRule->name; $auth->add($conventionContact); $auth->addChild($conventionContact, $accessConvention); $checkinManager = $auth->createRole('checkinManager'); $checkinManager->ruleName = $checkinManagerRule->name; $auth->add($checkinManager); $auth->addChild($checkinManager, $accessConvention); } /** * {@inheritdoc} */ public function safeDown() { echo "m250331_034538_init_convention_rbac cannot be reverted.\n"; return false; } /* // Use up()/down() to run migration code without a transaction. public function up() { } public function down() { echo "m250331_034538_init_convention_rbac cannot be reverted.\n"; return false; } */ }
5、现在角色有 3 个:companyEmployee、conventionContact、checkinManager,皆具有权限 accessConvention。且检查一个用户是否满足角色的规则已经定义完成。
6、下一步不计划手动指定用户的角色,而是基于默认角色,自动指定。参考:使用默认角色 https://www.yiiframework.com/doc/guide/2.0/zh-cn/security-authorization#using-default-roles 在配置 authManager 时指定 yii\rbac\BaseManager::$defaultRoles 选项 。如图1
return [ // ... 'components' => [ 'authManager' => [ 'class' => 'yii\rbac\DbManager', // 'cache' => 'cache', 'defaultRoles' => ['companyEmployee', 'conventionContact', 'checkinManager'], ], // ... ], ];
7、检查当前用户是否具有 accessConvention 权限。确认检查通过,具有 accessConvention 权限。通过 ‘can()’ 方法会检查角色和权限。用户拥有角色或其继承角色,符合预期。如图2
if (Yii::$app->user->can('accessConvention', ['convention' => $convention])) { $roles = Yii::$app->authManager->getRolesByUser(Yii::$app->user->id); print_r($roles); } if (Yii::$app->user->can('companyEmployee', ['convention' => $convention])) { // 'can()' 方法会检查角色和权限 echo '用户拥有 \'companyEmployee\' 角色或其继承角色'; } if (Yii::$app->user->can('conventionContact', ['convention' => $convention])) { // 'can()' 方法会检查角色和权限 echo '用户拥有 \'conventionContact\' 角色或其继承角色'; } if (Yii::$app->user->can('checkinManager', ['convention' => $convention])) { // 'can()' 方法会检查角色和权限 echo '用户拥有 \'checkinManager\' 角色或其继承角色'; }
SELECT * FROM `auth_assignment` WHERE `user_id`='1826937119386043' SELECT * FROM `auth_item` WHERE `name`='accessConvention' SELECT `parent` FROM `auth_item_child` WHERE `child`='accessConvention' SELECT * FROM `auth_item` WHERE `name`='checkinManager' SELECT `data` FROM `auth_rule` WHERE `name`='isCheckinManager' SELECT * FROM `users` WHERE `id`='1826937119386043' SELECT EXISTS(SELECT * FROM `convention_checkin_managers` WHERE (`convention_id`='1826937119386051') AND (`user_id`='1826937119386043') AND (`phone`='13980074657')) SELECT * FROM `auth_item` WHERE `name`='companyEmployee' SELECT `data` FROM `auth_rule` WHERE `name`='isCompanyEmployee' SELECT EXISTS(SELECT * FROM `company_employee_mapping` WHERE (`user_id`='1826937119386043') AND (`company_id`='1826937119386040')) SELECT `b`.* FROM `auth_assignment` `a`, `auth_item` `b` WHERE (`a`.`item_name`=`b`.`name`) AND (`a`.`user_id`='1826937119386043') AND (`b`.`type`=1)
8、检查当前用户是否具有 accessConvention 权限。确认检查不通过,无任何输出。不具有 accessConvention 权限。通过 ‘can()’ 方法会检查角色和权限。用户拥有角色或其继承角色,符合预期。如图3
SELECT * FROM `auth_assignment` WHERE `user_id`='1827082629726231' SELECT * FROM `users` WHERE `id`='1827082629726231' SELECT EXISTS(SELECT * FROM `convention_checkin_managers` WHERE (`convention_id`='1826937119386051') AND (`user_id`='1827082629726231') AND (`phone`='15609979522')) SELECT EXISTS(SELECT * FROM `company_employee_mapping` WHERE (`user_id`='1827082629726231') AND (`company_id`='1826937119386040')) SELECT * FROM `users` WHERE `id`='1827082629726231' SELECT EXISTS(SELECT * FROM `convention_contacts` WHERE (`convention_id`='1826937119386051') AND (`user_id`='1827082629726231') AND (`phone`='15609979522')) SELECT EXISTS(SELECT * FROM `company_employee_mapping` WHERE (`user_id`='1827082629726231') AND (`company_id`='1826937119386040')) SELECT * FROM `users` WHERE `id`='1827082629726231' SELECT EXISTS(SELECT * FROM `convention_contacts` WHERE (`convention_id`='1826937119386051') AND (`user_id`='1827082629726231') AND (`phone`='15609979522')) SELECT * FROM `users` WHERE `id`='1827082629726231' SELECT EXISTS(SELECT * FROM `convention_checkin_managers` WHERE (`convention_id`='1826937119386051') AND (`user_id`='1827082629726231') AND (`phone`='15609979522'))
1 条回复
[…] […]