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 异常。从另一方面来说,这并没有什么问题,因为从技术上讲,对于这个用户来说,这个模型不存在(因为他不是它的作者)。