将Captcha添加到Symfony2登录页面

tig*_*ris 11 captcha login symfony fosuserbundle

我是Symfony2的新手,但非常了解它.首先,我使用的是symfony 2.1.7.和FOSUserBundle用于用户设置.我已经使用用户名和密码覆盖了fos_user-login模板.但我想为登录添加验证码.我见过GregwarCaptchaBundle,根据文档,应该在FormType中添加新字段.我的问题是:symfony或FOSUserBundle登录表单类型在哪里,我可以添加这个新字段,还是覆盖它?存在ChangePasswordFormType,ProfileFormType ...等但没有LoginFOrmType.可能它是如此明显,但我没有得到重点,欢迎任何帮助请问
问题是解决方案的一些解决方案
看看下面的评论,帕特帮助了我.我用_username,_passwordcaptcha字段创建了一个新的表单类型.当用户名和密码命名以下划线开头时,足以进行"login_check"路由和Symfony身份验证.但是,Symfony使用侦听器进行登录过程.哪个是UsernamePasswordFormAuthenticationListener班级.虽然我在Form类型中添加了captcha字段,但在登录过程中总是会被忽略.(它在页面上呈现,但该字段永远不会被验证,只会被忽略.)

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');
}
Run Code Online (Sandbox Code Playgroud)

正如我上面提到的,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;
}
Run Code Online (Sandbox Code Playgroud)

captcha字段已添加.

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));
}
Run Code Online (Sandbox Code Playgroud)

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

我找到了另一个可能有用的答案[link] 是否存在任何类型的"登录前"事件或类似事件?

遵循此解决方案,我只需覆盖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));
        }



    }
Run Code Online (Sandbox Code Playgroud)

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

Mic*_*ick 11

Adding Captcha to Symfony2 Login Page
Run Code Online (Sandbox Code Playgroud)

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

Where is the symfony or FOSUserBundle login form type?
Run Code Online (Sandbox Code Playgroud)

登录没有表单类型.您可以在login.html.twig中看到该表单直接嵌入到模板中.

How could you do it?
Run Code Online (Sandbox Code Playgroud)

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


程序将是这样的:

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

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

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


编辑:回答您的评论

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

我强烈推荐这个阅读,看看如何远离基本控制器.现在,你怎么能这样做?

那么,你有两个选择:

选择1:方便

$form = $this->createForm(new LoginFormType(), null);
Run Code Online (Sandbox Code Playgroud)

变为:

$form = $this->get('form.factory')->create(new LoginFormType(), $null);
Run Code Online (Sandbox Code Playgroud)

选项2:将表格注册为服务

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

2.将表单定义为服务acme_user.login.form.你有一个很好的例子,在这里(在1.2版本FOSUserBundle的,登记和配置文件的形式注册为服务,所以这给你它是如何做一个很好的例子).

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

$form = $this->container->get('acme_user.login.form');
Run Code Online (Sandbox Code Playgroud)