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

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

问题描述

我是 Symfony2 的新手,但对它的了解非常多.首先,我使用的是 symfony 2.1.7.和 FOSUserBundle 用于用户设置.我已经用用户名和密码覆盖了 fos_user-login 模板.但是我想添加一个用于登录的验证码.我看过GregwarCaptchaBundle,根据文档,应该在FormType中添加新字段.我的问题来了:symfony 或 FOSUserBundle 登录表单类型在哪里,我可以添加这个新字段,或者覆盖它?存在 ChangePasswordFormType、ProfileFormType...等,但没有 LoginFOrmType.可能很明显,但我没有明白这一点,欢迎任何帮助
以某种方式使用解决方案编辑问题
看看下面帕特帮助我的评论.我创建了一个带有 _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

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

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 TCATStaffBundleListener;

use SymfonyComponentSecurityHttpFirewallUsernamePasswordFormAuthenticationListener as BaseListener; use SymfonyComponentFormExtensionCsrfCsrfProviderCsrfProviderInterface; use SymfonyComponentHttpFoundationRequest; use SymfonyComponentHttpKernelLogLoggerInterface; use SymfonyComponentSecurityHttpAuthenticationAuthenticationFailureHandlerInterface; use SymfonyComponentSecurityHttpAuthenticationAuthenticationSuccessHandlerInterface; use SymfonyComponentSecurityHttpSessionSessionAuthenticationStrategyInterface; use SymfonyComponentSecurityHttpHttpUtils; use SymfonyComponentSecurityCoreAuthenticationAuthenticationManagerInterface; use SymfonyComponentSecurityCoreAuthenticationTokenUsernamePasswordToken; use SymfonyComponentSecurityCoreExceptionInvalidCsrfTokenException; use SymfonyComponentSecurityCoreSecurityContextInterface; use SymfonyComponentEventDispatcherEventDispatcherInterface; use SymfonyComponentSecurityCoreExceptionBadCredentialsException;


    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: TCATStaffBundleListenerStaffLoginFormListener 行添加到 app/config/paramaters.yml顺便说一句,我可以检查我的验证码值.我希望这一切对你有用.

and add security.authentication.listener.form.class: TCATStaffBundleListenerStaffLoginFormListener 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(你可以看看 此处 查看类似内容).您需要覆盖 loginAction 方法,以便您可以将表单传递给您的模板 此处.

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

如何在扩展的控制器中访问表单容器感知?

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?

好吧,你有两个选择:

$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天全站免登陆