Как настроить пользовательские ответы отказа в доступе
Дата обновления перевода 2023-09-26
Как настроить пользовательские ответы отказа в доступе
В Symfony, вы можете вызвать AccessDeniedException, чтобы запретить доступ к пользователю. Symfony обработает это исключение, и сгенерирует ответ, основанный на состоянии аутентификации:
- Если пользователь не аутентифицирован (или аутентифицирован анонимно), точка входа аутентификации используется для генерирования ответа (обычно, перенаправления к странице входа в систему, или ответу 401 Неавторизовано;
- Если пользователь аутентифицирован, но не имеет необходимых разрешений, генерируется ответ 403 Запрещено.
Настройте ответ отказа в авторизации
Вам нужно создать класс, реализующий
AuthenticationEntryPointInterface.
Этот интерфейс имеет один метод (start()
), который вызывается, когда неаутентифицированный
пользователь пытается получить доступ к защищенному источнику:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
// src/Security/AuthenticationEntryPoint.php
namespace App\Security;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
class AuthenticationEntryPoint implements AuthenticationEntryPointInterface
{
public function __construct(
private UrlGeneratorInterface $urlGenerator,
) {
}
public function start(Request $request, AuthenticationException $authException = null): RedirectResponse
{
// добавьте пользовательское флэш-сообщение, и перенаправьте на страницу входа в систему
$request->getSession()->getFlashBag()->add('note', 'You have to login in order to access this page.');
return new RedirectResponse($this->urlGenerator->generate('security_login'));
}
}
Это все, если вы используете конфигурацию services.yaml по умолчанию . Иначе, вам нужно зарегистрировать этот сервис в контейнере.
Теперь, сконфигурируйте этот ID сервиса, в качестве точки входа в файерволл:
1 2 3 4 5 6 7
# config/packages/security.yaml
firewalls:
# ...
main:
# ...
entry_point: App\Security\AuthenticationEntryPoint
Настройте ответ "запрещено"
Создайте класс, который реализует
AccessDeniedHandlerInterface.
Этот интерфейс определяет один метод под названием handle()
, где вы
можете реализовывать любую логику, которую необходимо, когда текущему пользователю
в доступе отказано (например, отправить письмо, сделать запись лога сообщения, или
просто вернуть пользовательский ответ):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
// src/Security/AccessDeniedHandler.php
namespace App\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Http\Authorization\AccessDeniedHandlerInterface;
class AccessDeniedHandler implements AccessDeniedHandlerInterface
{
public function handle(Request $request, AccessDeniedException $accessDeniedException): ?Response
{
// ...
return new Response($content, 403);
}
}
Если вы используете конфигурацию services.yaml по умолчанию , вы закончили! Symfony автоматически узнает о вашем сервисе. Затем, вы сможете сконфигурировать его под своим брандмауэром:
1 2 3 4 5 6 7
# config/packages/security.yaml
firewalls:
# ...
main:
# ...
access_denied_handler: App\Security\AccessDeniedHandler
Настройка всех ответов отказа в доступе
В некоторых случаях, вам может захотеть и настроить ответы, и выполнить
какое-то действие (например, вход в систему) для каждого AccessDeniedException
.
В таком случае, сконфигурируйте слушатель kernel.exception :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
// src/EventListener/AccessDeniedListener.php
namespace App\EventListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
class AccessDeniedListener implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
// приоритет должен быть больше, чем HTTP Security
// ExceptionListener, чтобы он вызывался до слушателя
// исключений по умолчанию
KernelEvents::EXCEPTION => ['onKernelException', 2],
];
}
public function onKernelException(ExceptionEvent $event): void
{
$exception = $event->getThrowable();
if (!$exception instanceof AccessDeniedException) {
return;
}
// ... выполнить какое-то действие (например, запись логов)
// по желанию, установить пользовательский ответ
$event->setResponse(new Response(null, 403));
// или прекратить распространение (предотвращает вызов следующих слушателей исключений)
//$event->stopPropagation();
}
}