Класс Voter
Класс Voter используется для проверки прав доступа пользователя
к определённым действиям или объектам. Он вызывается системой
авторизации Symfony, когда требуется проверить, может ли текущий
пользователь выполнить действие над ресурсом. Класс наследуется
от Voter и переопределяет методы supports и
voteOnAttribute. Первый метод определяет, поддерживает ли
Voter данный атрибут и субъект, а второй - выполняет саму проверку.
Синтаксис
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
class CustomVoter extends Voter
{
protected function supports(string $attribute, mixed $subject): bool
{
}
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
{
}
}
Пример
Давайте создадим Voter, который проверяет, может ли пользователь
редактировать статью. Статья будет представлена сущностью
Article с полем author:
<?php
namespace App\Security;
use App\Entity\Article;
use App\Entity\User;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
class ArticleVoter extends Voter
{
public const EDIT = 'ARTICLE_EDIT';
public const VIEW = 'ARTICLE_VIEW';
protected function supports(string $attribute, mixed $subject): bool
{
if (!in_array($attribute, [self::EDIT, self::VIEW])) {
return false;
}
if (!$subject instanceof Article) {
return false;
}
return true;
}
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
{
$user = $token->getUser();
if (!$user instanceof User) {
return false;
}
/** @var Article $article */
$article = $subject;
return match ($attribute) {
self::VIEW => $this->canView($article, $user),
self::EDIT => $this->canEdit($article, $user),
default => false,
};
}
private function canView(Article $article, User $user): bool
{
return true;
}
private function canEdit(Article $article, User $user): bool
{
return $user === $article->getAuthor();
}
}
Пример
Теперь используем Voter в контроллере для проверки прав на редактирование статьи:
<?php
namespace App\Controller;
use App\Entity\Article;
use App\Security\ArticleVoter;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
class ArticleController extends AbstractController
{
#[Route('/article/{id}/edit', name: 'article_edit')]
#[IsGranted(ArticleVoter::EDIT, subject: 'article')]
public function edit(Article $article): Response
{
return new Response('article edited');
}
}
Если пользователь не является автором статьи, Symfony вернёт ответ с кодом 403:
Access Denied
Пример
Также Voter можно вызывать вручную через метод
isGranted контроллера:
<?php
namespace App\Controller;
use App\Entity\Article;
use App\Security\ArticleVoter;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class ArticleController extends AbstractController
{
#[Route('/article/{id}', name: 'article_show')]
public function show(Article $article): Response
{
if (!$this->isGranted(ArticleVoter::VIEW, $article)) {
throw $this->createAccessDeniedException();
}
return new Response('article shown');
}
}
Смотрите также
-
класс
Security,
который предоставляет доступ к сервисам авторизации -
метод
isGranted,
который проверяет права через Voter -
метод
supports,
который определяет поддержку атрибута и субъекта -
метод
voteOnAttribute,
который выполняет проверку прав доступа