首页 > 解决方案 > 使用 Yii2 RBAC 处理 Restful API 请求

问题描述

我开发了一个基于 Yii2 框架的网络应用程序。Web 应用程序根据用户类型(管理员、员工、子员工)使用 RBAC 系统进行操作授权。现在我正在开发一个移动应用程序,并为此移动应用程序调用的控制器创建了一个新模块“移动”。在这些新控制器中,我使用 CORS 和身份验证器设置了行为功能,这些都可以正常工作。我还设置了 RBAC 系统,就像我为 web 应用程序所做的那样,但在移动模块中不起作用。有人可以帮我设置控制器/操作的授权吗?

public function behaviors()
    {
        $behaviors = parent::behaviors();

        $behaviors['authenticator'] = [
            'class' => CompositeAuth::className(),
            'except' => ['index','view','test'],
            'authMethods' => [
                HttpBearerAuth::className(),
                HttpBasicAuth::className(),
                // QueryParamAuth::className(),
            ],
        ];

        $auth = $behaviors['authenticator'];
        unset($behaviors['authenticator']);

        $behaviors['corsFilter'] =
        [
            'class' => \yii\filters\Cors::className(),
            'cors' => [
                // restrict access to
                'Origin' => ['*'],
                // Allow only POST and PUT methods
                'Access-Control-Request-Method' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
                // // Allow only headers 'X-Wsse'
                'Access-Control-Request-Headers' => ['*'],
                // // Allow credentials (cookies, authorization headers, etc.) to be exposed to the browser
                'Access-Control-Allow-Credentials' => false,
                // // Allow OPTIONS caching
                'Access-Control-Max-Age' => 3600,
                // // Allow the X-Pagination-Current-Page header to be exposed to the browser.
                'Access-Control-Expose-Headers' => ['X-Pagination-Current-Page'],
            ],

        ];



        $behaviors['authenticator'] = $auth;
        // avoid authentication on CORS-pre-flight requests (HTTP OPTIONS method)
        // $behaviors['authenticator']['except'] = ['OPTIONS', 'login'];
        $behaviors['access'] = 
        [
            'class' => AccessControl::className(),
            'rules' => [
                [
                    'allow' => true,
                    'actions' => ['create','view','update','delete','index', 'logout'],
                    'roles' => ['@'],
                    'denyCallback' => function ($rule, $action) {
                        throw new \yii\web\ForbiddenHttpException('You are not allowed to access this page');
                    }
                ],
                [
                    'allow' => true,
                    'actions' => ['login', 'index','test'],
                    'roles' => ['?'],
                    'denyCallback' => function ($rule, $action) {
                        throw new \yii\web\ForbiddenHttpException('You are not allowed to access this page');
                    }
                ],
            ],

        ];

        return $behaviors;
    }

标签: phprestapiyii2rbac

解决方案


覆盖checkAccess()方法ActiveController()

$behaviors['access']不是在使用时检查访问的正确方法yii\rest\ActiveController,而是希望您覆盖该checkAccess()方法。

文档在此处此处

如何做到这一点的一个例子:

/**
 * Checks the privilege of the current user.
 *
 * This method should be overridden to check whether the current user has the privilege
 * to run the specified action against the specified data model.
 * If the user does not have access, a [[ForbiddenHttpException]] should be thrown.
 *
 * @param string $action the ID of the action to be executed
 * @param \yii\base\Model $model the model to be accessed. If `null`, it means no specific model is being accessed.
 * @param array $params additional parameters
 * @throws ForbiddenHttpException if the user does not have access
 */
public function checkAccess($action, $model = null, $params = [])
{
    // You could completely block some actions
    if ($action === 'delete') {

        throw new ForbiddenHttpException(
            Yii::t('app',
                'You are not allowed to {action} client models.',
                ['action' => $action]
            )
        );

    }

    // You could check if the current user has permission to run the action
    if ($action === 'index' && !Yii::$app->user->can('listClients')) {

        throw new ForbiddenHttpException(Yii::t('app',
            'You are not allowed to list clients'));

    }

    // You can also make the check more granular based on the model being accessed
    if ($action === 'view' && 
        !Yii::$app->user->can('viewClient', ['client_id' => $model->id])) {

        throw new ForbiddenHttpException(Yii::t('app',
            'You are not allowed to view client {client}',
            ['client' => $model->id]));            

    }
}

查看您的示例,您似乎只检查经过身份验证的用户@或未经过身份验证的用户、来宾、?.

这有点令人困惑,因为它在 上有所不同yii\web\Controller,但是您不应该检查用户是否在 上进行checkAccess()了身份验证,过滤器已经authenticator使用您在问题中发布的代码执行了该检查,当checkAccess()被调用时,您将始终拥有应用程序用户,因此@始终匹配,并且?永远不会匹配。

由于您已注释掉以下行:

// $behaviors['authenticator']['except'] = ['OPTIONS', 'login'];

这意味着 CORS 飞行前请求将始终失败,并且访客用户将永远无法登录。任何未通过身份验证的请求都将立即得到401 unauthorized响应。

似乎您正试图让所有经过身份验证的用户访问所有操作,而未经身份验证的用户仅访问loginindextest操作。如果正确,则不需要使用该checkAccess()方法,只需取消注释上述行并在其中添加操作,如下所示:

$behaviors['authenticator']['except'] = ['options', 'login', 'index', 'test'];

未经身份验证的用户将只能访问这些操作。


推荐阅读