将验证码添加到Symfony2登录页面 [英] Adding Captcha to Symfony2 Login Page

查看:62
本文介绍了将验证码添加到Symfony2登录页面的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是Symfony2的新手,但对此非常了解. 首先,我正在使用symfony 2.1.7.和FOSUserBundle进行用户设置.我已经用用户名和密码覆盖了fos_user-login模板.但是我想添加一个用于登录的验证码.我已经看到了GregwarCaptchaBundle,并且根据文档,应该将新字段添加到FormType中.我的问题来了:symfony或FOSUserBundle登录表单类型在哪里,我可以添加此新字段或覆盖它?存在ChangePasswordFormType,ProfileFormType ...等,但没有LoginFOrmType.也许是如此明显,但我没有明白这一点,欢迎任何帮助,请
使用某种解决方案编辑问题
看看下面Patt帮助我的评论. 我用_username_passwordcaptcha字段创建了一个新的表单类型.当用户名和密码的命名以下划线开头时,就足以进行"login_check"路由和Symfony身份验证.但是,Symfony使用侦听器进行登录过程. 这是UsernamePasswordFormAuthenticationListener类.尽管我在Form类型中添加了验证码字段,但在登录过程中始终将其忽略.(在页面上呈现该字段,但从未验证该字段,因此将其忽略).

I am new to Symfony2 but read about it very much. First of all, I am using symfony 2.1.7. And FOSUserBundle for user settings. I have already override fos_user-login template, with username and password. But I want to add a captcha for log in. I have seen GregwarCaptchaBundle, and according to document, new field should be added to FormType. And my question comes: Where is the symfony or FOSUserBundle login form type, that i can add this new field, or override it? There exists ChangePasswordFormType, ProfileFormType... etc. but no LoginFOrmType. May be it is so obvious but i did not get the point, Any help is welcomed please
QUESTION IS EDITED WITH A SOLUTION SOMEHOW
Take a look at the comments below that Patt helped me. I have created a new form type with _username, _password and captcha fields. When naming for username and password begins with an underscore is enough for 'login_check' routing and Symfony authentication. However Symfony uses a listener for login process. Which is UsernamePasswordFormAuthenticationListenerclass. Although i've added captcha field in the Form type, it is always ignored during login process.(It is rendered on the page, but the field is never validated, it is simply ignored.)

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类获取表单输入值,然后重定向您:

As i mentioned above UsernamePasswordFormAuthenticationListener class gets the form input values and then redirects you:

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代码不是一个好方法.如果我找到某种方法来覆盖并调用我自己的函数,则将其发布.
另一个有用的答案

Now, i have captcha on login screen. Playing with symfony code is not a good way, i know. If i find out some way to override and call my own function, i'll post it.
ANOTHER USEFUL ANSWER

我发现了另一个可能有用的答案 [link] 是是否有任何类型的预登录";事件还是类似事件?

I found another answer that might be useful [link]Is there any sort of "pre login" event or similar?

按照此解决方案,我仅覆盖了UsernamePasswordFormAuthenticationListener类并覆盖了安全侦听器security.authentication.listener.form.class参数.代码如下:

Following this solution, I have simply override UsernamePasswordFormAuthenticationListenerclass and override security listener security.authentication.listener.form.class parameter. Here goes the code:

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 顺便说一句,我可以检查我的验证码值.希望一切对您有用.

and add security.authentication.listener.form.class: TCAT\StaffBundle\Listener\StaffLoginFormListener line to the app/config/paramaters.yml BTW i can check my captcha value. I hope it all work for you.

推荐答案

Adding Captcha to Symfony2 Login Page

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

I am not sure this is a great idea. But it's doable.

Where is the symfony or FOSUserBundle login form type?

没有用于登录的表单类型.表单直接嵌入模板中,如您在 login.html.twig .

There is no form type for the login. The form is directly embed in the template as you can see in login.html.twig.

How could you do it?

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

You could totally create one but you would have to customize the SecurityController so that you send your form to the template.

过程如下:

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

1. Create your custom loginFormType (that's where you can add your captcha in the builder).

2. 覆盖SecurityController(您可以看一下此处.

2. Override the SecurityController (you could take a look here to see something similar). You need to override the loginAction method so that you can pass the form to your template here.

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

3. Override login.html.twig to render the form passed from your controller

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

How can you access to your form in a controller that extends ContainerAware?

我强烈推荐此阅读,以了解如何远离基本控制器.现在,您该怎么做?

I highly recommend this reading to see how you can move away from the base controller. Now, how can you do this?

好吧,您有2个选择:

$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版本中,注册和配置文件表单都被注册为服务,因此这为您提供了一个完美的示例).

2. Define your form as a service acme_user.login.form. You have a great example here (In the 1.2 version of FOSUserBundle, both registration and profile forms were registered as services, so this gives you a perfect example of how it's done).

3. 现在,您可以在扩展ContainerAware的控制器中使用表单.请参见此处.

$form = $this->container->get('acme_user.login.form');

这篇关于将验证码添加到Symfony2登录页面的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆