如何获取当前防火墙的check_path?

时间:2017-08-23 12:32:38

标签: symfony

问题:如何通过指定的防火墙名称获取form_login.check_path

我们订阅Symfony\Component\Security\Http\SecurityEvent::INTERACTIVE_LOGIN以便在具有多个防火墙的应用程序内记录成功登录。

一个防火墙通过Guard身份验证使用JWT令牌,这会产生负面影响,即使用有效令牌为每个请求触发此事件。

我们目前通过手动检查当前路线是否与防火墙的检查路径相匹配并停止事件传播以及提前返回来解决此问题。

由于我们正在添加更多防火墙(使用不同的令牌),我想更广泛地解决这个问题。因此,我想检查当前路由是否与当前防火墙检查路径匹配,而不用对任何路由或防火墙名称进行硬编码。

有一个类可以为Twig logout_path()方法使用的当前防火墙生成注销URL ,该方法以某种方式从防火墙侦听器获取注销路径/路径。 (Symfony\Component\Security\Http\Logout\LogoutUrlGenerator

在我进入一个长时间的调试会议之前,我想也许有人在此之前解决了这个问题;)

有什么想法吗?

示例代码:

class UserEventSubscriber implements EventSubscriberInterface
{

    /** @var LoggerInterface */
    protected $logger;

    /** @var FirewallMapInterface|FirewallMap */
    protected $firewallMap;

    public function __construct(LoggerInterface $logger, FirewallMapInterface $firewallMap)
    {
        $this->logger = $logger;
        $this->firewallMap = $firewallMap;
    }

    public function onInteractiveLogin(InteractiveLoginEvent $event)
    {
        $request = $event->getRequest();
        $firewallName = $this->firewallMap->getFirewallConfig($request)->getName();
        $routeName = $request->get('_route');

        if (('firewall_jwt' === $firewallName) && ('firewall_jwt_login_check' !== $routeName)) {
            $event->stopPropagation();
            return;
        }

        $this->logger->info(
            'A User has logged in interactively.',
            array(
                'event' => SecurityEvents::INTERACTIVE_LOGIN,
                'user' => $event->getAuthenticationToken()->getUser()->getUuid(),
        ));

1 个答案:

答案 0 :(得分:0)

check_path选项仅在身份验证工厂/侦听器中可用,因此您可以在容器构建时手动将此配置传递给订阅者类。

此解决方案考虑到check_path可能是路由名称或路径,这也是注入HttpUtils服务的原因:

namespace AppBundle\Subscriber;

use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Http\FirewallMapInterface;
use Symfony\Component\Security\Http\HttpUtils;
use Symfony\Component\Security\Http\SecurityEvents;

class UserEventSubscriber implements EventSubscriberInterface
{
    private $logger;
    private $httpUtils;
    private $firewallMap;
    private $checkPathsPerFirewall;

    public function __construct(LoggerInterface $logger, HttpUtils $httpUtils, FirewallMapInterface $firewallMap, array $checkPathsPerFirewall)
    {
        $this->logger = $logger;
        $this->httpUtils = $httpUtils;
        $this->firewallMap = $firewallMap;
        $this->checkPathsPerFirewall = $checkPathsPerFirewall;
    }

    public function onInteractiveLogin(InteractiveLoginEvent $event)
    {
        $request = $event->getRequest();
        $firewallName = $this->firewallMap->getFirewallConfig($request)->getName();
        $checkPath = $this->checkPathsPerFirewall[$firewallName];

        if (!$this->httpUtils->checkRequestPath($request, $checkPath)) {
            $event->stopPropagation();

            return;
        }

        $this->logger->info('A User has logged in interactively.', array(
            'event' => SecurityEvents::INTERACTIVE_LOGIN,
            'user' => $event->getAuthenticationToken()->getUser()->getUsername(),
        ));
    }

    public static function getSubscribedEvents()
    {
        return [SecurityEvents::INTERACTIVE_LOGIN => 'onInteractiveLogin'];
    }
}

在将此订户注册为服务(AppBundle\Subscriber\UserEventSubscriber)后,我们需要在您的DI扩展中实施PrependExtensionInterface,以便能够访问安全配置并使用每个防火墙的检查路径完成订户定义:

namespace AppBundle\DependencyInjection;

use AppBundle\Subscriber\UserEventSubscriber;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\Extension;
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;

class AppExtension extends Extension implements PrependExtensionInterface
{
    // ...

    public function prepend(ContainerBuilder $container)
    {
        $checkPathsPerFirewall = [];

        $securityConfig = $container->getExtensionConfig('security');
        foreach ($securityConfig[0]['firewalls'] as $name => $config) {
            if (isset($config['security']) && false === $config['security']) {
                continue; // skip firewalls without security
            }

            $checkPathsPerFirewall[$name] = isset($config['form_login']['check_path'])
                ? $config['form_login']['check_path']
                : '/login_check'; // default one in Symfony
        }

        $subscriber = $container->getDefinition(UserEventSubscriber::class);
        $subscriber->setArgument(3, $checkPathsPerFirewall);
    }
}

我希望它符合你的需要。