Введение Здравствуйте, уважаемые хабровчане.
Сегодня представляю вашему вниманию заключительную часть серии статей о расширенной авторизации действий с ресурсами в Laravel. Чтобы лучше понять, о чем пойдет речь в этой статье, вам необходимо прочитать первый И второй части.
На этот раз постановка задачи немного изменилась: У нас уже есть авторизация действий с ресурсами путем авторизации методов соответствующих контроллеров.
Задача состоит в том, чтобы разрешить изменение/просмотр определенных полей модели.
Также необходимо реализовать возможность авторизации редактирования/просмотра своих моделей.
Этот материал предназначен для практикующих программистов, и начинающему разработчику будет сложно его понять.
Часть 4. Авторизация действий с атрибутами
Теоретическая часть
Для реализации этого функционала я планирую использовать встроенные трейты Laravel. HasAttributes, HidesAttributes, GuardsAttributes .Используя эти черты, фреймворк управляет массовым заполнением и отображением атрибутов.
Возможность принудительного заполнения/чтения атрибутов я планирую оставить, так как если мы полностью авторизуем все действия по чтению/записи, то, скорее всего, нарушим функциональность программы.
Для реализации задачи нам понадобится:
- Переопределить методы получитьСкрытый(), получитьВидимый() чтобы скрыть неавторизованные поля.
- Переопределить метод isFillable() для защиты от несанкционированного проникновения в поля.
- Определите свой собственный метод аутентификацияUpdate() который вернет массив полей, защищенных от записи.
- Определите свой собственный метод авторизация() который вернет массив полей, защищенных от чтения.
- Переопределить метод полностью защищен () так как в результате добавления логики защиты этот метод может сработать ложно.
У каждого метода есть свои плюсы и минусы.
Но для меня главное, чтобы не было избыточности логики.
То есть постарайтесь избежать проблемы Банановые обезьяньи джунгли и облегчить получателям поддержку проекта.
А поскольку защита полей от записи/чтения достаточно точная, я реализовал черту.
Но это дело вкуса.
Laravel предоставляет возможность воспользоваться следующими свойствами:
- $видимый
- $скрыто
- $охраняется
- $заполняемый
Когда вы переопределяете поле $видимый система считает, что все остальные поля скрыты.
По аналогии — когда вы переопределяете поле $охраняется система считает, что все остальные поля разрешены к заполнению.
Для сохранения обратной совместимости уже написанного кода проекта эту логику следует не менять, а использовать.
Давайте начнем практиковаться
Было бы неплохо определиться с системой хранения дополнительных трейтов/хелперов/сервисов во фреймворке, а затем соответствовать собственным требованиям.Для черт я использую путь приложение/Расширения/Трейты , поэтому я создаю новую черту приложение/Расширения/Трейты/GuardedModel .
Это не системный путь, и не имеет значения, где именно хранится этот функционал (как, например, было с политиками).
В признаке я определяю методы авторизация() (разрешить просмотр) и аутентификацияUpdate() (разрешить редактирование), который вернет пустые массивы и может быть переопределен в целевой модели.
Далее вам нужно добавить в свой сервис встроенные методы.
getVisible() И получитьСкрытый() .
Но перед этим нужно решить, как именно будут скрываться атрибуты.
Предлагаю создать свой метод фильтрВидимость() , который будет запущен до выполнения родительского (встроенного) метода фреймворка.
Метод фильтрВидимость() сначала добавит все защищенные поля в массив $скрыто с помощью встроенного механизма сделатьСкрытый() , проходим по массиву защищенных полей, проверяя доступность каждого нашим методом userCanViewAttribute ($ключ) и формируем массив разрешенных.
Впоследствии разрешайте авторизованные поля с помощью встроенного механизма.
makeVisible() Для определения доступности редактирования воспользуемся стандартным методом isFillable ($ключ) , который перед вызовом родительского метода проверит необходимость авторизации ввода этого поля, и при необходимости проверит доступность нашего метода userCanUpdateAttribute ($ключ) Методы userCanUpdateAttribute ($ключ) И userCanViewAttribute ($ключ) выполнит проверку авторизации.
В моем случае используя метод $user-> может() библиотеки Spatie/Laravel-разрешение .
Вы, как я писал ранее, вольны использовать свои методы.
Черта Защищенная модель
Стоит дополнительно отметить необходимость переопределить метод полностью защищен () .<Э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; } }
Этот метод отвечает за выдачу исключения Illuminate\Database\Eloquent\MassAssignmentException в случаях, когда осуществляется попытка заполнения поля, когда все поля защищены и не открыты для заполнения.
Таким образом, если все поля недоступны для заполнения пользователем, будет сгенерировано исключение там, где его быть не должно.
Также обратите внимание на вызов метода $user-> can("обновление- атрибут -$key-".
static::class) , а именно на формирование названия разрешения.
Оно аналогично названию разрешения для всей модели, только с добавлением приставки атрибут .
Именно по этому принципу необходимо будет получить разрешение во время первоначального посева/использования.
Давайте расширим модель нашей чертой и определим методы.
авторизация() И аутентификацияUpdate() .
Например, он вернет поля пользователь И ID пользователя .
Я сознательно взял атрибут ID пользователя чтобы продемонстрировать, что пользователь И ID пользователя представлены разными полями.
И нужно отдельно ограничить их просмотр.
Также, например, я добавил поля $охраняемый, $скрытый, $заполняемый , который необходим для настройки массового заполнения модели.
Модель Сообщения
<Э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. Авторизация действий с вашими ресурсами
Сразу к практике
Данный функционал лишь немного расширяет уже имеющийся.Чтобы реализовать это, нам нужно определить соединение в целевой модели.
пользователь() и в процессе миграции добавьте поле ID пользователя .
Далее вам нужно немного изменить политики.
Мы расширим методы, которые принимают экземпляр модели.
Например вот так $user-> can('delete-self-'.
$this-> getModelClass()) .
Но прежде чем проверять, можно ли удалять свои модели, необходимо узнать владельца модели.
Для этого создадим в политике метод isOwner(Пользователь $user, Модель $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;
}
}
Также отмечу, что во избежание ошибок при выполнении стоит ввести проверку доступности метода.
пользователь() или аналогичный в целевой модели.
Это все на данный момент. Я надеюсь, что эта статья была полезна.
У нас достаточно полная и гибкая система авторизации действий.
Если будут вопросы, обязательно отвечу.
И, конечно же, конструктивные комментарии или предложения также приветствуются.
При желании, более подробно с реализацией данного образовательного проекта вы можете ознакомиться в репозитории .
Теги: #Разработка веб-сайта #php #Laravel #разрешения #атрибуты #владелец модели
-
Программное Обеспечение Ипотечного Брокера
19 Oct, 24 -
Микрофронтенд-Архитектура, Например Angular
19 Oct, 24 -
Полезные Мелочи В Дата-Центре: Wi-Fi Ip Kvm
19 Oct, 24 -
Инновации Мобильных Операторов.
19 Oct, 24