我创建了用于http错误显示的Custom Twig模板,以通过扩展基本布局来使网站设计统一。 (与常规错误消息不同,我想保留导航菜单并显示错误)
它可以正常工作,但适用于404。
在基本布局的导航菜单中,根据用户的权限,我有很多is_granted('SOME_ROLES')
用于显示网站的可用部分。抛出404时,导航菜单显示为好像用户断开连接:{% if is_granted("IS_AUTHENTICATED_REMEMBERED") %}
为假。
经过一些搜索,我发现路由器是在防火墙之前执行的。由于在抛出404时找不到路由,因此不会执行防火墙,也不会将权限发送到模板。
我发现(source from 2014)的唯一解决方法是在route.yaml文件的最底部添加此路由定义:
pageNotFound:
path: /{path}
defaults:
_controller: App\Exception\PageNotFound::pageNotFound
由于其他所有路线均不匹配,因此应该找不到该路线。
控制器:
<?php
namespace App\Exception;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class PageNotFound
{
public function pageNotFound()
{
return (new NotFoundHttpException());
}
}
因为执行了控制器,所以执行了防火墙,并且显示了404错误页面(如我所愿(呼啦!)。
我的问题是:有什么正确方法可以解决该问题,而不是解决方法?
答案 0 :(得分:1)
我们遇到了类似的问题。
example.com/supersecretarea/
,我们希望未经授权的用户访问example.com/supersecretarea/
之后的任何URL时都得到403错误代码,即使在页面不存在的事件。 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
,则不需要此特定修复程序。
编辑:
要使Symfony使用我的路由器和异常侦听器版本,我在process()
的{{1}}方法中添加了以下代码:
Kernel.php