简体   繁体   中英

Adding Captcha to Symfony2 Login

I need to add Captcha to my login page, I am using GregwarCaptchaBundle and FosUserBundle.

For the moment I have get show the captcha on the login using the following code:

<?php

/*
 * This file is part of the FOSUserBundle package.
 *
 * (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace FOS\UserBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Gregwar\Captcha\CaptchaBuilder;

class SecurityController extends Controller
{
    public function loginAction(Request $request)
    {
        $builtCaptcha = new CaptchaBuilder();
        $builtCaptcha->build();
        $builtCaptcha->save('captcha.jpg');
        /** @var $session \Symfony\Component\HttpFoundation\Session\Session */
        $session = $request->getSession();

        if (class_exists('\Symfony\Component\Security\Core\Security')) {
            $authErrorKey = Security::AUTHENTICATION_ERROR;
            $lastUsernameKey = Security::LAST_USERNAME;
        } else {
            // BC for SF < 2.6
            $authErrorKey = SecurityContextInterface::AUTHENTICATION_ERROR;
            $lastUsernameKey = SecurityContextInterface::LAST_USERNAME;
        }

        // get the error if any (works with forward and redirect -- see below)
        if ($request->attributes->has($authErrorKey)) {
            $error = $request->attributes->get($authErrorKey);
        } elseif (null !== $session && $session->has($authErrorKey)) {
            $error = $session->get($authErrorKey);
            $session->remove($authErrorKey);
        } else {
            $error = null;
        }

        if (!$error instanceof AuthenticationException) {
            $error = null; // The value does not come from the security component.
        }

        // last username entered by the user
        $lastUsername = (null === $session) ? '' : $session->get($lastUsernameKey);

        if ($this->has('security.csrf.token_manager')) {
            $csrfToken = $this->get('security.csrf.token_manager')->getToken('authenticate')->getValue();
        } else {
            // BC for SF < 2.4
            $csrfToken = $this->has('form.csrf_provider')
                ? $this->get('form.csrf_provider')->generateCsrfToken('authenticate')
                : null;
        }
        $t = $request->get('_captcha');
        if($t!=$builtCaptcha){
            echo 'error';
        }
        var_dump($t);

        return $this->renderLogin(array(
            'last_username' => $lastUsername,
            'error' => $error,
            'csrf_token' => $csrfToken,
            'captcha' => $builtCaptcha,
        ));
    }

    /**
     * Renders the login template with the given parameters. Overwrite this function in
     * an extended controller to provide additional data for the login template.
     *
     * @param array $data
     *
     * @return \Symfony\Component\HttpFoundation\Response
     */
    protected function renderLogin(array $data)
    {
        return $this->render('FOSUserBundle:Security:login.html.twig', $data);
    }

    public function checkAction($builtCaptcha)
    {
            return $this->redirect($this->generateUrl('fos_user_login'));
        }
        throw new \RuntimeException('You must configure the check path to be handled by the firewall using form_login in your security firewall configuration.');
    }

    public function logoutAction()
    {
        throw new \RuntimeException('You must activate the logout in your security firewall configuration.');
    }
}

And I overrided the login and register template as the documentation explain (the registration is working fine with the captcha)

The problem is that I don't know how to validate the captcha's code.

I guess that I should do it into the checkAction()

But I am not sure, I am very caught with this problem.

If someone could help me I would be very grateful, Thanks in advance.

I had the same problem when trying to implement Google ReCaptcha into a simple login-form with database provider. This is a working solution based on a listener triggered by SecurityEvents::INTERACTIVE_LOGIN. This event is fired after verification of credentials but before redirecting to default_target_path defined in security.yml.

Note: The example is mixed with some other functionality, because I am also capturing failed login attempts.

<?php

namespace ExampleBundle\EventListener;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken;
use Symfony\Component\Security\Core\AuthenticationEvents;
use Symfony\Component\Security\Core\Event\AuthenticationFailureEvent;
use Symfony\Component\Security\Core\Event\AuthenticationEvent;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Http\SecurityEvents;
use ExampleBundle\Google\GoogleReCaptcha;
use Doctrine\ORM\EntityManager;
use ExampleBundle\Entity\User;

/**
 * Class AuthenticationAttemptListener
 * @package ExampleBundle\EventListener
 */
class AuthenticationAttemptListener implements EventSubscriberInterface
{
    /**
     * @var EntityManager
     */
    private $entityManager;

    /**
     * @var GoogleReCaptcha
     */
    private $googleReCaptcha;

    /**
     * @var integer
     */
    private $maxFailedLoginAttempts;

    /**
     * AuthenticationAttemptListener constructor.
     *
     * @param EntityManager $entityManager
     * @param GoogleReCaptcha $googleReCaptcha
     * @param integer $maxFailedLoginAttempts
     */
    public function __construct(EntityManager $entityManager, GoogleReCaptcha $googleReCaptcha, $maxFailedLoginAttempts)
    {
        $this->entityManager = $entityManager;
        $this->googleReCaptcha = $googleReCaptcha;
        $this->maxFailedLoginAttempts = $maxFailedLoginAttempts;
    }

    /**
     * @return array
     */
    public static function getSubscribedEvents()
    {
        return array(
            AuthenticationEvents::AUTHENTICATION_FAILURE => 'onAuthenticationFailure',
            AuthenticationEvents::AUTHENTICATION_SUCCESS => 'onAuthenticationSuccess',
            SecurityEvents::INTERACTIVE_LOGIN => 'onInteractiveLogin'
        );
    }

    /**
     * Count failed login attempts and save to database on existing usernames
     *
     * @param AuthenticationFailureEvent $event
     */
    public function onAuthenticationFailure(AuthenticationFailureEvent $event)
    {
        if ($event->getAuthenticationException() instanceof BadCredentialsException) {

            $databaseUser = $this->searchUserinDatabase($event);

            // increase failed attempt counter or lock-up account
            if ($databaseUser !== null) {
                if (!$databaseUser->isEnabled()) {
                    throw new CustomUserMessageAuthenticationException('user_deactivated');
                } else {
                    $databaseUser->increaseFailedLoginAttempts();
                    $databaseUser->setFailedLoginLastTimestamp(new \DateTime());
                    if ($databaseUser->getFailedLoginAttempts() == $this->maxFailedLoginAttempts) {
                        $databaseUser->setIsActive(0);
                    }

                    $this->entityManager->persist($databaseUser);
                    $this->entityManager->flush();
                }
            }
        }
    }

    /**
     * @param AuthenticationSuccessEvent $event
     */
    public function onAuthenticationSuccess(AuthenticationEvent $event)
    {
        // Attention: Event will be thrown on every request if you have session-based authentication!
        // Reset of attempt counter may occur if user is logged in while brute force attack is running
    }

    /**
     * Check incoming google recaptcha token and reset attempt-counter on success or throw exception
     *
     * @param InteractiveLoginEvent $event
     */
    public function onInteractiveLogin(InteractiveLoginEvent $event)
    {
        $reCaptchaResponse = $event->getRequest()->get('g-recaptcha-response');
        $captchaRequest = $this->googleReCaptcha->checkReCaptcha($reCaptchaResponse);

        if (is_array($captchaRequest) && $captchaRequest['success'] === true) {
            // reset attempt counter because of successful login and positive recaptcha response
            $databaseUser = $this->searchUserinDatabase($event);

            if ($databaseUser !== null && $databaseUser->isEnabled()) {
                $databaseUser->setFailedLoginAttempts(null);
                $databaseUser->setFailedLoginLastTimestamp(null);

                $this->entityManager->persist($databaseUser);
                $this->entityManager->flush();
            }
        } else {
            // on all other recaptcha related errors throw exception
            throw new CustomUserMessageAuthenticationException('recaptcha_error');
        }
    }

    /**
     * Retrieve user from database
     *
     * @param AuthenticationFailureEvent|AuthenticationEvent $event
     *
     * @return User|null
     */
    private function searchUserinDatabase($event)
    {
        $token = $event->getAuthenticationToken();
        $username = $token->getUsername();
        $databaseUser = null;

        if (!$token instanceof AnonymousToken) {
            // get user from database
            $databaseUser = $this->entityManager->getRepository($entity)->findOneBy(array(
                "username" => $username
            ));
        }

        return $databaseUser;
    }
}

Hope that helps ...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM