【发布时间】:2017-03-16 20:16:51
【问题描述】:
在 Symfony 中,当用户尝试访问该特定用户(根据用户角色)禁止访问的路由时,将返回响应代码为 403 的页面。
所以用户仍然可以看到那里有一条有效的路线。
我想通过将状态码 403 替换为 404 来覆盖此行为,因此当不允许用户访问该资源时,用户只会看到没有有效的路由。
我怎样才能做到这一点?
【问题讨论】:
标签: php symfony security permissions
在 Symfony 中,当用户尝试访问该特定用户(根据用户角色)禁止访问的路由时,将返回响应代码为 403 的页面。
所以用户仍然可以看到那里有一条有效的路线。
我想通过将状态码 403 替换为 404 来覆盖此行为,因此当不允许用户访问该资源时,用户只会看到没有有效的路由。
我怎样才能做到这一点?
【问题讨论】:
标签: php symfony security permissions
最后我找到了一个更简单的解决方案:使用拒绝访问处理程序。
不幸的是,没有太多关于如何创建拒绝访问处理程序的文档,但它非常简单。
首先创建一个实现AccessDeniedHandlerInterface的类并将其设置为service(例如将其命名为my_access_denied_handler_service)。
在 handle 方法中,应该创建并返回一个 Response(在我的例子中,我想要一个 404 响应)。
那么在security.yml配置文件中我们要设置access_denied_handler下firewall:
...
firewalls:
my_firewall:
...
access_denied_handler: my_access_denied_handler_service
...
...
【讨论】:
另一种解决方案是覆盖 Symfony 安全组件的 AccessListener 服务。
here 记录了有关如何覆盖捆绑服务的通用过程。以下是关于这种特殊情况的具体例子。
首先让我们创建一个覆盖AccessListener类的类:
<?php
namespace Path\To\My\Bundle\Services;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Security\Http\Firewall\AccessListener;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
class OverrideAccessListener extends AccessListener
{
public function handle(GetResponseEvent $event)
{
try {
parent::handle($event);
} catch (AccessDeniedException $e) {
$request = $event->getRequest();
$message = sprintf('No route found for "%s %s"', $request->getMethod(), $request->getPathInfo());
if ($referer = $request->headers->get('referer')) {
$message .= sprintf(' (from "%s")', $referer);
}
throw new NotFoundHttpException($message);
}
}
}
那么我们需要创建一个Compiler Pass,以便用新的类更改原始服务的类属性:
<?php
namespace Path\To\My\Bundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class OverrideServiceCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$definition = $container->getDefinition('security.access_listener');
$definition->setClass('Path\To\My\Bundle\Services\OverrideAccessListener');
}
}
最后我们需要register the Compiler Pass in the build method of the bundle:
<?php
namespace Path\To\My\Bundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Path\To\My\Bundle\DependencyInjection\Compiler\OverrideServiceCompilerPass;
class MyBundleName extends Bundle
{
public function build(ContainerBuilder $container)
{
parent::build($container);
$container->addCompilerPass(new OverrideServiceCompilerPass());
}
}
【讨论】:
正如here 部分解释的那样,一种可能的解决方案如下:
1) 在services.yml中定义一个新的服务控制器
exception_controller:
class: Path\To\MyBundle\Controller\MyExceptionController
arguments: ['@twig', '%kernel.debug%']
2) 创建覆盖Symfony\Bundle\TwigBundle\Controller\ExceptionController 的新类:
namespace Path\To\MyBundle\Controller;
use Symfony\Bundle\TwigBundle\Controller\ExceptionController;
use Symfony\Component\Debug\Exception\FlattenException;
use Symfony\Component\HttpKernel\Log\DebugLoggerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class MyExceptionController extends ExceptionController
{
public function showAction(Request $request, FlattenException $exception, DebugLoggerInterface $logger = null)
{
$currentContent = $this->getAndCleanOutputBuffering($request->headers->get('X-Php-Ob-Level', -1));
$showException = $request->attributes->get('showException', $this->debug); // As opposed to an additional parameter, this maintains BC
$code = $exception->getStatusCode();
if ($code == 403) {
$code = 404;
// other customizations ...
}
return new Response($this->twig->render(
(string) $this->findTemplate($request, $request->getRequestFormat(), $code, $showException),
array(
'status_code' => $code,
'status_text' => isset(Response::$statusTexts[$code]) ? Response::$statusTexts[$code] : '',
'exception' => $exception,
'logger' => $logger,
'currentContent' => $currentContent,
)
));
}
}
3) 在config.yml下twig下设置如下:
twig:
exception_controller: 'exception_controller:showAction'
尽管我最初的目标是完全避免使用该代码引发此类异常。
【讨论】:
这是可行的,但几乎没有记录。我知道两种方法,但可能还有更多:
使用access_denied_url 配置选项。见security config reference。使用此选项,您可以设置当用户未经授权时重定向用户的 URL(我认为它也应该与路由名称一起使用)。查看类似问题:Symfony2 Redirection for unauthorised page with access_denied_url
还有The Firewall and Authorization 中提到的“入口点”。但是,没有例子,没有解释如何使用它。
我看起来这个选项需要一个服务名称,如 security config reference 所示(搜索 entry_point 选项)。
【讨论】: