У нас была похожая проблема.
- Мы хотели получить доступ к токену аутентификации на страницах ошибок.
- В сценарии, когда часть веб-сайта находится за брандмауэром,скажем
example.com/supersecretarea/
, мы хотели, чтобы неавторизованные пользователи получили код ошибки 403 при доступе к любому URL за example.com/supersecretarea/
, даже в том случае, если страница не существует .Поведение Symfony не позволяет этого и проверяет наличие 404 (либо потому, что нет маршрута, либо потому, что у маршрута есть параметр, который не разрешен, например example.com/supersecretarea/user/198
, когда пользователь отсутствует 198
).
В итоге мы переопределили маршрутизатор по умолчанию в Symfony (Symfony\Bundle\FrameworkBundle\Routing\Router
), чтобы изменить его поведение:
public function matchRequest(Request $request): array
{
try {
return parent::matchRequest($request);
} catch (ResourceNotFoundException $e) {
// Ignore this next line for now
// $this->targetPathSavingStatus->disableSaveTargetPath();
return [
'_controller' => 'App\Controller\CatchAllController::catchAll',
'_route' => 'catch_all'
];
}
}
CatchAllController
просто отображает страницу ошибки 404:
public function catchAll(): Response
{
return new Response(
$this->templating->render('bundles/TwigBundle/Exception/error404.html.twig'),
Response::HTTP_NOT_FOUND
);
}
Что происходит, когда во время обычного процесса на маршрутизаторе Symfony, если что-то должно вызывать ошибку 404, мы ловим это исключение в функции matchRequest
.Предполагается, что эта функция возвращает информацию о том, какое действие контроллера нужно выполнить для отображения страницы, поэтому мы и делаем: мы сообщаем маршрутизатору, что мы хотим отобразить страницу 404 (с кодом 404).Вся защита обрабатывается между matchRequest
возвратом и catchAll
вызовом, поэтому брандмауэры могут вызвать ошибки 403, у нас есть токен аутентификации и т. Д.
Существует как минимум одна функциональная проблемак этому подходу (что нам удалось исправить на данный момент).Symfony имеет дополнительную систему, которая запоминает последнюю страницу, которую вы пытались загрузить, поэтому, если вы будете перенаправлены на страницу входа и успешно войдете в систему, вы будете перенаправлены на ту страницу, которую пытались загрузить изначально.Когда брандмауэр генерирует исключение, это происходит:
// Symfony\Component\Security\Http\Firewall\ExceptionListener
protected function setTargetPath(Request $request)
{
// session isn't required when using HTTP basic authentication mechanism for example
if ($request->hasSession() && $request->isMethodSafe(false) && !$request->isXmlHttpRequest()) {
$this->saveTargetPath($request->getSession(), $this->providerKey, $request->getUri());
}
}
Но теперь, когда мы разрешаем несуществующим страницам инициировать перенаправления брандмауэра на страницу входа (скажем, example.com/registered_users_only/*
перенаправляет на страницу загрузки, ипользователь, не прошедший проверку подлинности, нажимает example.com/registered_users_only/page_that_does_not_exist
), мы абсолютно не хотим сохранять эту несуществующую страницу как новый «TargetPath» для перенаправления после успешного входа в систему, в противном случае пользователь увидит, по-видимому, случайную ошибку 404.Мы решили расширить прослушиватель исключений setTargetPath
и определили службу, которая переключает, должен ли целевой слушатель сохранять исключительный путь.
// Our extended ExceptionListener
protected function setTargetPath(Request $request): void
{
if ($this->targetPathSavingStatus->shouldSave()) {
parent::setTargetPath($request);
}
}
Это цель закомментированной строки $this->targetPathSavingStatus->disableSaveTargetPath();
сверху: чтобы отключить состояние по умолчанию для сохранения целевого пути в исключениях брандмауэра, когда есть 404 (переменные targetPathSavingStatus
здесь указывают на очень простой сервис, используемый только для хранения этой части информации).
Эта часть решения не очень удовлетворительная.Я хотел бы найти что-то лучше.Похоже, на данный момент она действительно выполняет свою работу.
Конечно, если у вас есть от always_use_default_target_path
до true
, тогда это конкретное исправление не требуется.
EDIT:
Чтобы Symfony использовал мои версии прослушивателя Router и Exception, я добавил следующий код в методе process()
Kernel.php
:
public function process(ContainerBuilder $container)
{
// Use our own CatchAll router rather than the default one
$definition = $container->findDefinition('router.default');
$definition->setClass(CatchAllRouter::class);
// register the service that we use to alter the targetPath saving mechanic
$definition->addMethodCall('setTargetPathSavingStatus', [new Reference('App\Routing\TargetPathSavingStatus')]);
// Use our own ExceptionListener so that we can tell it not to use saveTargetPath
// after the CatchAll router intercepts a 404
$definition = $container->findDefinition('security.exception_listener');
$definition->setClass(FirewallExceptionListener::class);
// register the service that we use to alter the targetPath saving mechanic
$definition->addMethodCall('setTargetPathSavingStatus', [new Reference('App\Routing\TargetPathSavingStatus')]);
// ...
}