在Symfony中,当用户尝试访问该特定用户禁止的路由时(根据用户角色),将返回包含响应代码403的页面。
所以用户仍然可以看到那里有一条有效的路线。
我想通过将状态代码403替换为404来覆盖此行为,因此当不允许用户访问该资源时,用户将看到没有有效路由。
我该如何实现?
答案 0 :(得分:0)
这是可行的,但几乎没有记录。我知道有两种方法,但可能会有更多:
使用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
选项)。
答案 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