【问题标题】:How to replace all 403 status codes with 404 in Symfony如何在 Symfony 中用 404 替换所有 403 状态码
【发布时间】:2017-03-16 20:16:51
【问题描述】:

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

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

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

我怎样才能做到这一点?

【问题讨论】:

标签: php symfony security permissions


【解决方案1】:

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

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

首先创建一个实现AccessDeniedHandlerInterface的类并将其设置为service(例如将其命名为my_access_denied_handler_service)。

handle 方法中,应该创建并返回一个 Response(在我的例子中,我想要一个 404 响应)。

那么在security.yml配置文件中我们要设置access_denied_handlerfirewall:

...
firewalls:
    my_firewall:
        ...
        access_denied_handler: my_access_denied_handler_service
        ...
...

【讨论】:

    【解决方案2】:

    另一种解决方案是覆盖 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());
        }
    }
    

    【讨论】:

      【解决方案3】:

      正如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.ymltwig下设置如下:

      twig:
          exception_controller: 'exception_controller:showAction'
      

      尽管我最初的目标是完全避免使用该代码引发此类异常。

      【讨论】:

        【解决方案4】:

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

        【讨论】:

          猜你喜欢
          • 2020-05-31
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-05-06
          • 2020-07-27
          • 1970-01-01
          • 2023-04-02
          • 1970-01-01
          相关资源
          最近更新 更多