11

我是 Symfony2 的新手,但读了很多。首先,我使用的是 symfony 2.1.7。和 FOSUserBundle 用于用户设置。我已经使用用户名和密码覆盖了 fos_user-login 模板。但我想为登录添加验证码。我看过 GregwarCaptchaBundle,根据文档,应该在 FormType 中添加新字段。我的问题来了:symfony 或 FOSUserBundle 登录表单类型在哪里,我可以添加这个新字段,或者覆盖它?存在 ChangePasswordFormType、ProfileFormType...等,但没有 LoginFOrmType。可能很明显,但我没有明白这一点,欢迎任何帮助,请用某种
解决方案编辑问题
看看下面的评论,帕特帮助了我。我创建了一个新的表单类型_username_password并且captcha字段。当用户名和密码的命名以下划线开始时,'login_check' 路由和 Symfony 身份验证就足够了。然而 Symfony 使用一个监听器来进行登录过程。哪个是UsernamePasswordFormAuthenticationListener类。虽然我在 Form 类型中添加了验证码字段,但在登录过程中它总是被忽略。(它在页面上呈现,但该字段从未验证,它只是被忽略。)

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('_username', 'email', array('label' => 'form.username', 'translation_domain' => 'FOSUserBundle')) // TODO: user can login with email by inhibit the user to enter username
        ->add('_password', 'password', array(
        'label' => 'form.current_password',
        'translation_domain' => 'FOSUserBundle',
        'mapped' => false,
        'constraints' => new UserPassword()))
        ->add('captcha', 'captcha');
}

正如我上面提到的UsernamePasswordFormAuthenticationListener类获取表单输入值,然后重定向你:

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, array_merge(array(
        'username_parameter' => '_username',
        'password_parameter' => '_password',
        'csrf_parameter'     => '_csrf_token',
        'captcha'           => 'captcha',
        'intention'          => 'authenticate',
        'post_only'          => true,
    ), $options), $logger, $dispatcher);

    $this->csrfProvider = $csrfProvider;
}

添加了验证码字段。

protected function attemptAuthentication(Request $request)
{
    if ($this->options['post_only'] && 'post' !== strtolower($request->getMethod())) {
        if (null !== $this->logger) {
            $this->logger->debug(sprintf('Authentication method not supported: %s.', $request->getMethod()));
        }

        return null;
    }

    if (null !== $this->csrfProvider) {
        $csrfToken = $request->get($this->options['csrf_parameter'], null, true);

        if (false === $this->csrfProvider->isCsrfTokenValid($this->options['intention'], $csrfToken)) {
            throw new InvalidCsrfTokenException('Invalid CSRF token.');
        }
    }

   // check here the captcha value
    $userCaptcha = $request->get($this->options['captcha'], null, true);
    $dummy = $request->getSession()->get('gcb_captcha');
    $sessionCaptcha = $dummy['phrase'];
   // if captcha is not correct, throw exception
    if ($userCaptcha !== $sessionCaptcha) {
        throw new BadCredentialsException('Captcha is invalid');
    }

    $username = trim($request->get($this->options['username_parameter'], null, true));
    $password = $request->get($this->options['password_parameter'], null, true);

    $request->getSession()->set(SecurityContextInterface::LAST_USERNAME, $username);

    return $this->authenticationManager->authenticate(new UsernamePasswordToken($username, $password, $this->providerKey));
}

现在,我在登录屏幕上有验证码。我知道,玩 symfony 代码不是一个好方法。如果我找到某种方法来覆盖并调用我自己的函数,我会发布它。
另一个有用的答案

我找到了另一个可能有用的答案 [链接]是否有任何类型的“预登录”事件或类似事件?

按照这个解决方案,我只需覆盖UsernamePasswordFormAuthenticationListener类并覆盖安全侦听器security.authentication.listener.form.class参数。代码如下:

namespace TCAT\StaffBundle\Listener;

use Symfony\Component\Security\Http\Firewall\UsernamePasswordFormAuthenticationListener as BaseListener; use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Log\LoggerInterface; use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface; use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface; use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface; use Symfony\Component\Security\Http\HttpUtils; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException; use Symfony\Component\Security\Core\SecurityContextInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Security\Core\Exception\BadCredentialsException;


    class StaffLoginFormListener extends BaseListener
    {
        private $csrfProvider;

        /**
         * {@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, array_merge(array(
                'username_parameter' => '_username',
                'password_parameter' => '_password',
                'csrf_parameter'     => '_csrf_token',
                'captcha'           => 'captcha',
                'intention'          => 'authenticate',
                'post_only'          => true,
            ), $options), $logger, $dispatcher);

            $this->csrfProvider = $csrfProvider;
        }

        /**
         * {@inheritdoc}
         */
        protected function attemptAuthentication(Request $request)
        {
            if ($this->options['post_only'] && 'post' !== strtolower($request->getMethod())) {
                if (null !== $this->logger) {
                    $this->logger->debug(sprintf('Authentication method not supported: %s.', $request->getMethod()));
                }

                return null;
            }

            if (null !== $this->csrfProvider) {
                $csrfToken = $request->get($this->options['csrf_parameter'], null, true);

                if (false === $this->csrfProvider->isCsrfTokenValid($this->options['intention'], $csrfToken)) {
                    throw new InvalidCsrfTokenException('Invalid CSRF token.');
                }
            }

            // throw new BadCredentialsException('Bad credentials');
            $userCaptcha = $request->get($this->options['captcha'], null, true);
            $dummy = $request->getSession()->get('gcb_captcha');
            $sessionCaptcha = $dummy['phrase'];

            if ($userCaptcha !== $sessionCaptcha) {
                throw new BadCredentialsException('Captcha is invalid');
            }

            $username = trim($request->get($this->options['username_parameter'], null, true));
            $password = $request->get($this->options['password_parameter'], null, true);

            $request->getSession()->set(SecurityContextInterface::LAST_USERNAME, $username);

            return $this->authenticationManager->authenticate(new UsernamePasswordToken($username, $password, $this->providerKey));
        }



    }

并将security.authentication.listener.form.class: TCAT\StaffBundle\Listener\StaffLoginFormListener行添加到 app/config/paramaters.yml 顺便说一句我可以检查我的验证码值。我希望这一切都对你有用。

4

2 回答 2

11
Adding Captcha to Symfony2 Login Page

我不确定这是个好主意。但这是可行的。

Where is the symfony or FOSUserBundle login form type?

登录没有表单类型。如您在login.html.twig中所见,表单直接嵌入到模板中。

How could you do it?

您完全可以创建一个,但您必须自定义SecurityController以便将表单发送到模板。


该过程将是这样的:

1.创建您的自定义loginFormType(您可以在构建器中添加您的验证码)。

2.覆盖SecurityController(你可以看看这里看到类似的东西)。您需要重写该loginAction方法,以便可以将表单传递到此处的模板。

3.覆盖login.html.twig以呈现从控制器传递的表单


编辑:回答您的评论

如何在扩展 ContainerAware 的控制器中访问表单?

我强烈推荐阅读这篇文章,了解如何远离基本控制器。现在,你怎么能做到这一点?

好吧,你有两个选择:

选项 1:简单的方法

$form = $this->createForm(new LoginFormType(), null);

变成:

$form = $this->get('form.factory')->create(new LoginFormType(), $null);

选项 2:注册表格即服务

1.创建您的 formType(正常过程):loginFormType

2.将您的表单定义为服务acme_user.login.form您在这里有一个很好的例子(在 FOSUserBundle 的 1.2 版本中,注册和配置文件表单都被注册为服务,所以这为您提供了一个很好的例子来说明它是如何完成的)。

3.您现在可以在扩展 ContainerAware 的控制器中使用您的表单。见这里

$form = $this->container->get('acme_user.login.form');
于 2013-02-10T11:13:03.057 回答
1

回应:我知道玩 symfony 代码不是一个好方法。如果我找到某种方法来覆盖并调用我自己的函数,我会发布它。

要覆盖“UsernamePasswordFormAuthenticationListenerclass”,您必须复制捆绑包中的侦听器文件并更改 config.yml 文件以加载新的:

parameters:
        security.authentication.listener.form.class: Acme\YourBundle\Security\UsernamePasswordFormAuthenticationListener 

此外,复制文件中的命名空间必须更改为正确的命名空间:

namespace Acme\YourBundle\Security;

最后一件事是在要正确加载的使用部分中添加“AbstractAuthenticationListener”:

use Symfony\Component\Security\Http\Firewall\AbstractAuthenticationListener;
于 2015-01-23T10:44:43.837 回答