【问题标题】:Restricting controller action to creator of post in Yii2将控制器操作限制为 Yii2 中的帖子创建者
【发布时间】:2016-08-30 17:16:58
【问题描述】:

有没有一种简单的方法可以在不使用完整的 RBAC 的情况下将控制器操作限制为帖子的所有者/创建者?

现在我正在为每个控制器执行此操作:

public function actionUpdate( $id ) {
    $model = $this->findModel( $id );
    if ( $model->user_id != Yii::$app->user->identity->id ) {
        throw new NotFoundHttpException( 'The requested page does not exist.' );
    }
}

但我认为必须有更好的方法将某些控制器限制为创建 $model 的用户,即正在编辑。

【问题讨论】:

    标签: yii2


    【解决方案1】:

    1) 推荐的方式是使用 RBAC 和规则。 official docs 在相应的专门部分中对此进行了很好的介绍。

    检查作者 ID 是否与通过参数传递的当前用户 ID 匹配的规则示例:

    namespace app\rbac;
    
    use yii\rbac\Rule;
    
    /**
     * Checks if authorID matches user passed via params
     */
    class AuthorRule extends Rule
    {
        public $name = 'isAuthor';
    
        /**
         * @param string|integer $user the user ID.
         * @param Item $item the role or permission that this rule is associated with
         * @param array $params parameters passed to ManagerInterface::checkAccess().
         * @return boolean a value indicating whether the rule permits the role or permission it is associated with.
         */
        public function execute($user, $item, $params)
        {
            return isset($params['post']) ? $params['post']->createdBy == $user : false;
        }
    }
    

    然后您需要将其与现有权限绑定(可以在迁移或扩展中完成):

    $auth = Yii::$app->authManager;
    
    // add the rule
    $rule = new \app\rbac\AuthorRule;
    $auth->add($rule);
    
    // add the "updateOwnPost" permission and associate the rule with it.
    $updateOwnPost = $auth->createPermission('updateOwnPost');
    $updateOwnPost->description = 'Update own post';
    $updateOwnPost->ruleName = $rule->name;
    $auth->add($updateOwnPost);
    
    // "updateOwnPost" will be used from "updatePost"
    $auth->addChild($updateOwnPost, $updatePost);
    
    // allow "author" to update their own posts
    $auth->addChild($author, $updateOwnPost);
    

    然后您可以检查您的用户是否可以像这样更新帖子:

    use yii\web\ForbiddenHttpException;
    use Yii;
    
    public function actionUpdate($id)
    {
        $model = $this->findModel($id);
        if (!Yii::$app->user->can('updatePost', ['post' => $model])) {
            throw new ForbiddenHttpException('You are not allowed to edit this post');
        }
    
        ...
    }
    

    另请注意,如果您首先找到模型并且用户无权编辑它,从逻辑上讲,最好抛出 403 Forbidden 异常而不是 404,因为它已找到,但不允许编辑。

    不要忘记在AccessControl 行为中包含这样的规则:

    [
        'allow' => true,
        'actions' => ['update'],
        'roles' => ['@'],
    ],
    

    表示该控制器的update动作只能被除guest外的授权用户访问。

    2)如果由于某种原因您不想使用 RBAC,您可以使用您的方法:

    use yii\web\ForbiddenHttpException;
    
    public function actionUpdate($id)
    {
        $model = $this->findModel($id);
        if ($model->user_id != Yii::$app->user->id ) {
            throw new ForbiddenHttpException('You are not allowed to edit this post.');
        }
    
        ...
    }
    

    为了改进这一点,您可以通过将此逻辑移动到辅助方法来从该检查中抽象出来:

    namespace app\posts\components;
    
    use Yii;
    
    class PostPermission
    {
        /**
         * @param $model Post
         * @return boolean
         */
        public static function allowedToUpdate($model)
        {
            return $model->user_id = Yii:$app->user->id;
        }
    }
    

    然后这样称呼它:

    use app\posts\components\PostPermission;
    use yii\web\ForbiddenHttpException;
    
    if (!PostPermission::allowedToUpdate($model) {
        throw new ForbiddenHttpException('You are not allowed to edit this post.');
    }
    

    这只是一个例子,方法不必是静态的,你可以使用$model构造实例。

    你可以直接在Post模型中创建方法,但最好不要用这种逻辑污染模型。

    3) 我可以建议的另一种选择是在查找模型时最初将范围限制为当前用户:

    use yii\web\NotFoundHttpException;
    
    /**
     * @param integer $id
     * @return Post
     * @throws NotFoundHttpException
     */
    protected function findModel($id)
    {
        $model = Post::find(['id'=> $id, 'user_id' => Yii::$app->user->id])->one();
        if ($model) {
            return $model;
        } else {
            throw new NotFoundHttpException('This post does not exist.');
        }
    }
    

    这可以为网站管理员改进:

    use yii\web\NotFoundHttpException;
    
    /**
     * @param integer $id
     * @return Post
     * @throws NotFoundHttpException
     */
    protected function findModel($id)
    {
        $query = Post::find()->where(['id' => $id]);
        if (!Yii::$app->user->is_admin) { // replace with your own check
            $query->andWhere(['user_id' => Yii::$app->user->id]);
        }
        $model = $query->one();
        if ($model) {
            return $model;
        } else {
            throw new NotFoundHttpException('This post does not exist.');
        }
    }
    

    那你只写:

    public function actionUpdate($id)
    {
        $model = $this->findModel($id);
        ...
    }
    

    在这两种情况下(模型未找到且当前用户不允许编辑),都会引发 404 Not Found 异常。从另一方面来说,这并没有什么问题,因为从技术上讲,对于这个用户来说,这个模型不存在(因为他不是它的作者)。

    【讨论】:

    • 谢谢你这么详细的回答
    【解决方案2】:

    我们可以使用

    访问控制过滤器

    用于限制控制器操作而不是 RBAC。如果仅通过denyCallback,下面的代码将授予对actionUpdate的访问权限。

    use yii\filters\AccessControl;
    
    class SiteController extends Controller
    {
        public function behaviors()
        {
            return [
                'access' => [
                    'class' => AccessControl::className(),
                    'only' => ['update','delete'],
                    'rules' => [
                        [
                            'actions' => ['update'],
                            'allow' => false,
                            'denyCallback' => function ($rule, $action) { //PHP callable that should be called when this rule will deny the access.
                                //Write your logic here to deny the action
                                throw new \Exception('You are not allowed to access this page');
                            }
                        ],
                    ],
                ],
            ];
        }
    
        public function actionUpdate()
        {
            return $this->render('update');
        }
    }
    

    供您参考https://github.com/yiisoft/yii2/blob/master/docs/guide/security-authorization.md

    【讨论】:

    • 我们需要先找到模型然后执行检查,但是访问控制在操作之前执行。或者我们最终可以使用重复代码来查找模型。另外,最好根据 HTTP 异常抛出,而不是泛型的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-09-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-09
    • 2021-09-23
    • 1970-01-01
    相关资源
    最近更新 更多