Product.supplierID = Supplier.supplierID
---------         ----------
|Product|---------|Supplier|
---------         ----------
                       |
                       |  Supplier.supplierID = User.supplierID
                       |
                   ---------
                   |  User |
                   ---------

使用上面的表结构,应用程序使用ActiveController的子类,并覆盖prepareDataProvider来限制登录index可以看到的每个ProductUser列表,以匹配具有supplierID值的用户。 actions()ProductController方法中类似的内容。
$actions['index']['prepareDataProvider'] = function($action)
{
    $query = Product::find();
    if (Yii::$app->user->can('supplier') &&
        Yii::$app->user->identity->supplierID) {
        $query->andWhere(['supplierID' => Yii::$app->user->identity->supplierID]);
    }
    return new ActiveDataProvider(['query' => $query]);
};

这工作正常,但是我正在寻找使用checkAccess()来限制单个actionView()Product

目前,无论是否具有适当的User,登录的Product都可以通过更改URL中的productID来访问supplierID

看来我无法访问Product的特定实例来检查supplierID,直到actionView()返回,这是我希望进行检查的时间。

我可以覆盖checkAccess()来限制访问并抛出适当的ForbiddenHttpException吗?

最佳答案

那么检查模型是否存在的函数呢?

protected function modelExist($id)
{
    return Product::find()
    ->where([ 'productID' => $id ])
    ->andWhere(['supplierID' => Yii::$app->user->identity->supplierID ])
    ->exists();
}

如果productID是您的产品主键,那么对/products/1的请求将由yii\rest\UrlRule转换为/products?productID=1

在这种情况下,当将productID作为参数提供时,您可以使用beforeAction快速检查该模型是否存在,并让该操作执行,否则,将引发错误:
// this array will hold actions to which you want to perform a check
public $checkAccessToActions   = ['view','update','delete'];

public function beforeAction($action) {
    if (!parent::beforeAction($action)) return false;

        $params = Yii::$app->request->queryParams;

        if (isset($params['productID']) {
           foreach ($this->checkAccessToActions as $action) {
              if ($this->action->id === $action) {
                  if ($this->modelExist($params['productID']) === false)
                       throw new NotFoundHttpException("Object not found");
              }
           }
    }
    return true;
}

更新

由于问题是关于的问题,因此在剩余的ActiveController中覆盖了checkAccess方法,我认为举个例子会很有用。

按照Yii2 REST的设计方式,一旦加载了模型实例,所有deleteupdateview操作将调用checkAccess方法:

// code snippet from yii\rest\ViewAction
$model = $this->findModel($id);
if ($this->checkAccess) {
    call_user_func($this->checkAccess, $this->id, $model);
}
createindex操作也是如此,只是它们不会将任何模型实例传递给它:call_user_func($this->checkAccess, $this->id)

因此,您尝试执行的操作(当用户尝试查看,更新或删除不是其供应商的产品时抛出ForbiddenHttpException)也可以通过以下方式实现:

public function checkAccess($action, $model = null, $params = [])
{
    if ($action === 'view' or $action === 'update' or $action === 'delete')
    {
        if ( Yii::$app->user->can('supplier') === false
             or Yii::$app->user->identity->supplierID === null
             or $model->supplierID !== \Yii::$app->user->identity->supplierID )
        {
             throw new \yii\web\ForbiddenHttpException('You can\'t '.$action.' this product.');
        }

    }
}

关于php - Yii2-覆盖其余ActiveController中的checkAccess,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/33168784/

10-10 14:43