Права доступа
Аутентификация отвечает на вопрос «кто это», а права доступа — «что ему можно». Самая частая задача — не пускать гостей на страницы добавления и удаления статей. Проверять это в каждом действии неудобно, поэтому проверку ставят в одно место, через которое проходит любой запрос.
Где ставить проверку
У класса модуля есть метод onBootstrap(): Laminas вызывает его при запуске. В нём можно подписаться на событие — шаг обработки запроса. Событие MvcEvent::EVENT_ROUTE происходит, когда маршрут уже определён, а контроллер ещё не вызван. Подробнее о событиях рассказывает отдельный урок.
Закрытые маршруты
Откройте module/Blog/src/Module.php и добавьте метод (если он уже есть после урока о сессиях — допишите в него):
// module/Blog/src/Module.php
use Laminas\Authentication\AuthenticationService;
use Laminas\Mvc\MvcEvent;
public function onBootstrap(MvcEvent $event): void
{
$events = $event->getApplication()->getEventManager();
$events->attach(MvcEvent::EVENT_ROUTE, function (MvcEvent $e) {
$match = $e->getRouteMatch();
if ($match === null) {
return;
}
$protected = ['blog/add', 'blog/edit', 'blog/delete'];
$auth = $e->getApplication()->getServiceManager()->get(AuthenticationService::class);
if (!in_array($match->getMatchedRouteName(), $protected, true) || $auth->hasIdentity()) {
return;
}
$response = $e->getResponse();
$response->getHeaders()->addHeaderLine('Location', $e->getRouter()->assemble([], ['name' => 'login']));
$response->setStatusCode(302);
return $response;
}, -100);
}Слушатель берёт имя сработавшего маршрута. Если оно в списке закрытых, а посетитель не вошёл, слушатель возвращает ответ с перенаправлением, и контроллер не вызывается. Число -100 — приоритет: отрицательное значение ставит слушателя после встроенного определения маршрута.
Выйдите через /logout и откройте /blog/add — браузер окажется на /login.
Роли и разрешения
Когда пользователей несколько видов, одного «вошёл или нет» мало. Пакет laminas-permissions-acl описывает список прав (ACL): роли, ресурсы и разрешения.
composer require laminas/laminas-permissions-acluse Laminas\Permissions\Acl\Acl;
$acl = new Acl();
$acl->addRole('guest');
$acl->addRole('editor', 'guest');
$acl->addRole('admin', 'editor');
$acl->addResource('post');
$acl->allow('guest', 'post', 'view');
$acl->allow('editor', 'post', ['add', 'edit']);
$acl->allow('admin');
$acl->isAllowed('editor', 'post', 'edit'); // true
$acl->isAllowed('editor', 'post', 'delete'); // falseВторой аргумент addRole() — родитель: editor может всё, что может guest. Вызов allow('admin') без ресурса разрешает роли всё.
| Понятие | Что это | Пример |
|---|---|---|
| Роль | Вид пользователя | guest, editor, admin |
| Ресурс | То, к чему ограничен доступ | post |
| Привилегия | Действие над ресурсом | view, edit, delete |
Объект Acl создают в фабрике и регистрируют как сервис, а в слушателе вместо hasIdentity() вызывают isAllowed() с ролью из личности пользователя. Для больших проектов есть похожий пакет laminas-permissions-rbac, где права привязаны к ролям без ресурсов.
Частая ошибка
Скрыть ссылку «Удалить» в шаблоне — не защита: адрес /blog/delete/5 можно набрать вручную. Права проверяют на сервере — в слушателе или в действии, — а скрытие ссылок только делает страницу удобнее.