覆盖复杂的 Symfony2 类 - 最佳实践


Overriding a complicated Symfony2 class - best practice

我正在使用Symfony 2.3 LTS,我认为它与最新版本略有不同(对于我问题的部分)。

我需要覆盖"security.authentication.listener.form"服务,即此类:https://github.com/symfony/Security/blob/2.3/Http/Firewall/UsernamePasswordFormAuthenticationListener.php

我只想添加一点代码,没什么大不了的。

这些是声明中重要的部分(在Symfony配置文件中):

    <parameter key="security.authentication.listener.form.class">Symfony'Component'Security'Http'Firewall'UsernamePasswordFormAuthenticationListener</parameter>
    <service id="security.authentication.listener.form"
             class="%security.authentication.listener.form.class%"
             parent="security.authentication.listener.abstract"
             abstract="true">
    </service>
    <service id="security.authentication.listener.abstract" abstract="true" public="false">
        <tag name="monolog.logger" channel="security" />
        <argument type="service" id="security.context" />
        <argument type="service" id="security.authentication.manager" />
        <argument type="service" id="security.authentication.session_strategy" />
        <argument type="service" id="security.http_utils" />
        <argument />
        <argument type="service" id="security.authentication.success_handler" />
        <argument type="service" id="security.authentication.failure_handler" />
        <argument type="collection"></argument>
        <argument type="service" id="logger" on-invalid="null" />
        <argument type="service" id="event_dispatcher" on-invalid="null" />
    </service>

还有两个额外的要点:

(1) I only have experience using Yaml and although it shouldn't be difficult converting this, it does add an additional obstacle to deal with. I will also use Yaml for my finished solution. I've never seen the on-invalid attribute though for a start.
(2) I need to pass in some additional parameters of my own.

我尝试只覆盖类名和基本的类扩展,看看它是否正常工作而没有错误,但我认为没有使用任何传入的值:

亚姆尔:

parameters:
  security.authentication.listener.form.class: MyBundle'Security'MyCustomUsernamePasswordFormAuthenticationListener

PHP类:

class MyCustomUsernamePasswordFormAuthenticationListener extends UsernamePasswordFormAuthenticationListener
{
/**
 * {@inheritdoc}
 */
public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, HttpUtils $httpUtils, $providerKey, AuthenticationSuccessHandlerInterface $successHandler, AuthenticationFailureHandlerInterface $failureHandler, array $options = array(), LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null, CsrfProviderInterface $csrfProvider = null)
{
    parent::__construct($securityContext, $authenticationManager, $sessionStrategy, $httpUtils, $providerKey, $successHandler, $failureHandler, $options, $logger, $dispatcher, $csrfProvider);
}
/**
 * {@inheritdoc}
 */
protected function attemptAuthentication(Request $request)
{
    parent::attemptAuthentication($request);
}

}

另外,我不明白为什么"security.authentication.listener.abstract"服务中的参数 5 为空,但如果它为空(但不是),该类将抛出错误。

另外,我没有看到security.authentication.listener.form服务作为安全配置(http://symfony.com/doc/2.3/reference/configuration/security.html)中的一个选项。如果没有,我可以像上面提到的那样覆盖,但如果可能的话,最好在 security.yml 中声明它。

那么,在

Yaml 中执行此操作的最佳实践方法是什么?我可以以某种方式破解它,剪切和粘贴等,但理想情况下,我不需要重新声明所有参数,因为它们已经声明了。

首先,要回答为什么在安全配置(http://symfony.com/doc/2.3/reference/configuration/security.html)中看不到security.authentication.listener.form服务作为选项:

  • 在 app/config/security.yml 中,您将找到安全捆绑包和组件的配置选项。这些选项可以从应用程序文件夹中的配置文件进行编辑。位于捆绑包中的配置,位于文件夹/Resources/config 内,只能使用 compilerPass 进行编辑。

现在到你的主要问题,解决方案取决于你想做什么:

  • 如果您直接使用该类,则可能需要将其发送给装饰器并根据需要使用该装饰器
  • 如果没有,您可以这样做:到您的服务.yml添加:

    acme.security.authentication.listener.form:
       class: %security.authentication.listener.form.class%
       parent: security.authentication.listener.abstract
       abstract: true
    

    然后在捆绑包中创建一个 CompilerPass,并将其添加到其中:

    public function process(ContainerBuilder $container)
    {
         $definition = new DefinitionDecorator( 'acme.security.authentication.listener.form' );
         $definition->setClass( 'Acme'Bundle'MyCustomUsernamePasswordFormAuthenticationListener' );
         $definition->setAbstract( true );
         // Here you can add your additional data
         $definition->setMethodCalls($callsArray);
         $container->setDefinition( 'security.authentication.listener.form', $definition );
    }
    
只有在安全

包之后启动捆绑包时,您才能在 Resources/config/services.yml 中覆盖没有编译器密码的security.authentication.listener.form。但这是我不推荐的做法。使用 CompilerPass 时,您将始终确保它在初始化所有捆绑包后运行。

希望这有帮助