如何在Symfony中用404替换所有403状态代码

时间:2016-11-03 09:44:17

标签: php symfony security permissions

在Symfony中,当用户尝试访问该特定用户禁止的路由时(根据用户角色),将返回包含响应代码403的页面。

所以用户仍然可以看到那里有一条有效的路线。

我想通过将状态代码403替换为404来覆盖此行为,因此当不允许用户访问该资源时,用户将看到没有有效路由。

我该如何实现?

4 个答案:

答案 0 :(得分:0)

这是可行的,但几乎没有记录。我知道有两种方法,但可能会有更多:

答案 1 :(得分:0)

一个可能的解决方案,如部分解释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'

尽管我最初的目标是阻止使用该代码抛出这样的异常。

答案 2 :(得分:0)

另一种解决方案是覆盖Symfony Security组件的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);
        }
    }
}

然后我们需要创建一个编译器传递,以便使用新类更改原始服务的class属性:

<?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());
    }
}

答案 3 :(得分:0)

最后,我找到了一个更简单的解决方案:使用访问被拒绝处理程序

不幸的是,没有太多关于如何创建访问被拒绝处理程序的文档,但它非常简单。

首先创建一个实现AccessDeniedHandlerInterface的类,并将其设置为service(例如命名为#include <iostream> #include <deque> #include <mutex> #include <memory> #include <chrono> #include <thread> using namespace std; class Event { public: Event(int a) : m_a(a) { } void print() { cout << m_a << endl; } private: int m_a; }; class EventQueue { public: unique_ptr<Event> pop() { lock_guard<mutex> lock(m_Mutex); unique_ptr<Event> p = nullptr; if (!m_Queue.empty()) { p = move(m_Queue.back()); m_Queue.pop_back(); } return p; } void push(unique_ptr<Event> p) { lock_guard<mutex> lock(m_Mutex); m_Queue.push_front(move(p)); } private: deque<unique_ptr<Event>> m_Queue; mutex m_Mutex; }; EventQueue evq; void EventHandler() { while (true) { unique_ptr<Event> p = evq.pop(); if (p) p->print(); else this_thread::sleep_for(chrono::seconds(10)); } } int main() { std::thread first (EventHandler); while (true) { int a; cin >> a; unique_ptr<Event> ev(new Event(a)); evq.push(move(ev)); } first.join(); return 0; } )。

my_access_denied_handler_service方法中,应创建并返回Response(在我的情况下,我想要404响应)。

然后在handle配置文件中,我们必须在security.yml下设置access_denied_handler

firewall