Laravel中的高级资源授权系统。第3部分。读取/写入属性,自己的模型

介绍


您好,亲爱的Khabrovchans。

今天,我提请您注意有关Laravel中使用资源进行操作的高级授权的系列文章的最后一部分。为了更好地理解本文将讨论的内容,您需要阅读第一部分第二部分。


这次,有关问题的陈述有所更改:我们已经通过相应控制器方法的授权来授权具有资源的动作。任务是授权修改/查看模型的特定字段。您还需要实现授权模型作者进行编辑/查看的功能。


该材料是供练习程序员使用的,对新手开发人员来说很难理解。



第4部分。具有属性的动作的授权


理论部分


为了实现此功能,我计划使用Laravel HasAttributes,HidesAttributes,GuardsAttributes的内置特征具有这些特征,框架控制属性的大量填充和显示。我计划保留强制填充/读取属性的功能,因为如果我完全授权所有读取/写入操作,很可能会中断程序的性能。


为了完成任务,我们需要:


  • 覆盖getHidden(),getVisible()方法以隐藏未授权的字段。
  • 重写isFillable()方法以防止未经授权的字段记录。
  • authUpdate() .
  • authView() .
  • totallyGuarded() .

— , , . . , . - Banana Monkey Jungle, . / — . .


Laravel :


  • $visible
  • $hidden
  • $guarded
  • $fillable

. $visible . — $guarded . — , .



// , . app/Extensions/Traits, app/Extensions/Traits/GuardedModel. , ( ).

authView()( ) authUpdate()( ), , .

getVisible() getHidden().

, . filterVisibility(), , () .

filterVisibility() $hidden makeHidden(), , userCanViewAttribute($key) . makeVisible()


isFillable($key), , userCanUpdateAttribute($key)


userCanUpdateAttribute($key) userCanViewAttribute($key) . $user->can() spatie/laravel-permission. , , .


GuardedModel


<?php

namespace App\Extensions\Traits;

use App\Models\User;
use Illuminate\Database\Eloquent\Model;

trait GuardedModel
{
    protected function authView(): array
    {
        return [];
    }

    protected function authUpdate(): array
    {
        return [];
    }

    public function getHidden(): array
    {
        $this->filterVisibility();

        return parent::getHidden();
    }

    public function getVisible(): array
    {
        $this->filterVisibility();

        return parent::getVisible();
    }

    public function isFillable($key)
    {
        if (in_array($key, $this->authView())) {
            return $this->userCanUpdateAttribute($key);
        }

        return parent::isFillable($key);
    }

    private function filterVisibility(): void
    {
        $this->makeHidden($this->authView());

        $authVisible = array_filter(
            $this->authView(),
            fn ($attr) => $this->userCanViewAttribute($attr)
        );

        $this->makeVisible($authVisible);
    }

    private function userCanViewAttribute(string $key): bool
    {
        /** @var User $user */
        $user = auth()->user();
        $ability = !empty($user) && $user->can("view-attr-$key-" . static::class);

        return $ability;
    }

    private function userCanUpdateAttribute(string $key): bool
    {
        /** @var User $user */
        $user = auth()->user();
        $ability = !empty($user) && $user->can("update-attr-$key-" . static::class);

        return $ability;
    }

    public function totallyGuarded()
    {
        $guarded = (
            count($this->getFillable()) === 0
            && count($this->authView()) === 0
            && $this->getGuarded() == ['*']
        );

        return  $guarded;
    }
}

totallyGuarded(). Illuminate\Database\Eloquent\MassAssignmentException , . — , .

$user->can(«update-attr-$key-». static::class), . , attr. /.


, authView() authUpdate(). user user_id. user_id, user user_id . . , , $guarded, $hidden, $fillable, .


Posts


<?php

namespace App\Models;

use App\Extensions\Traits\GuardedModel;
use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    use GuardedModel;

    protected $hidden = ['id'];

    protected $guarded = ['created_at', 'updated_at'];

    protected $fillable = ['title', 'description', 'user_id'];

    protected function authView(): array
    {
        return ['user_id', 'user'];
    }

    protected function authUpdate(): array
    {
        return ['user_id'];
    }

    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

5.



. user(), user_id. . , . $user->can('delete-self-'. $this->getModelClass()). , . isOwner(User $user, Model $model). . , , — .


一般模型政策政策


<?php

namespace App\Policies;

use App\Models\User;
use Illuminate\Auth\Access\HandlesAuthorization;
use Illuminate\Database\Eloquent\Model;

abstract class ModelPolicy
{
    use HandlesAuthorization;

    abstract protected function getModelClass(): string;

    public function delete(User $user, Model $model)
    {
        if ($user->can('delete-' . $this->getModelClass())) {
            return true;
        }

        if ($user->can('delete-self-' . $this->getModelClass())) {
            return $this->isOwner($user, $model);
        }

        return false;
    }

    private function isOwner(User $user, Model $model): bool
    {
        if (!empty($user) && method_exists($model, 'user')) {
            return $user->getKey() === $model->getRelation('user')->getKey();
        }

        return false;
    }
}

我还注意到,为了避免在运行时出错,值得介绍一下对目标模型中user()方法或类似方法的可用性进行检查


目前为止就这样了。希望本文对您有所帮助。我们有一个相当完整和灵活的行动授权系统。如果您有任何疑问,我一定会回答。当然,也欢迎提出建设性意见或建议。如果愿意,您可以在存储库中更详细地看到此培训项目的实施


All Articles