You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Symfony 3.4如何替换默认认证监听器集成Google reCAPTCHA?

Hey there! Let's tackle this reCAPTCHA integration with Symfony 3.4's security system. Since you've already got a custom authentication listener ready and know how to validate the reCAPTCHA, here are two solid approaches to get this working without relying on a third-party bundle:

Option 1: Replace the Default Form Authentication Listener (Symfony 3.x Compatible)

Symfony 3 removed the direct parameter override for the form authentication listener class, but you can use a Compiler Pass to swap out the default listener with your custom one. Here's how:

Step 1: Create a Compiler Pass Class

This pass will modify the default form authentication listener service to use your custom class:

// src/MyBundle/DependencyInjection/Compiler/MyAuthenticationListenerCompilerPass.php
namespace MyBundle\DependencyInjection\Compiler;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

class MyAuthenticationListenerCompilerPass implements CompilerPassInterface
{
    public function process(ContainerBuilder $container)
    {
        // Check if the default form listener service exists
        if ($container->hasDefinition('security.authentication.listener.form')) {
            $listenerDef = $container->getDefinition('security.authentication.listener.form');
            // Replace with your custom listener class
            $listenerDef->setClass('MyBundle\EventListener\MyAuthenticationListener');
        }
    }
}

Step 2: Register the Compiler Pass in Your Bundle

Add the pass to your bundle's build method to activate it:

// src/MyBundle/MyBundle.php
namespace MyBundle;

use MyBundle\DependencyInjection\Compiler\MyAuthenticationListenerCompilerPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;

class MyBundle extends Bundle
{
    public function build(ContainerBuilder $container)
    {
        parent::build($container);
        $container->addCompilerPass(new MyAuthenticationListenerCompilerPass());
    }
}

This will ensure your MyAuthenticationListener is used instead of the default UsernamePasswordFormAuthenticationListener for all form-based authentication firewalls.

Option 2: Add a Pre-Authentication Request Listener (Simpler Approach)

If you don't want to replace the entire listener, you can add a kernel.request listener that validates reCAPTCHA before the default authentication logic runs. This is quicker to implement:

Step 1: Create the Recaptcha Validation Listener

This listener will check the reCAPTCHA response only on your login check path:

// src/MyBundle/EventListener/RecaptchaValidationListener.php
namespace MyBundle\EventListener;

use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Security;

class RecaptchaValidationListener
{
    private $router;
    private $recaptchaSecretKey;

    public function __construct(RouterInterface $router, string $recaptchaSecretKey)
    {
        $this->router = $router;
        $this->recaptchaSecretKey = $recaptchaSecretKey;
    }

    public function onKernelRequest(GetResponseEvent $event)
    {
        $request = $event->getRequest();

        // Only handle POST requests to your login check path
        if ($request->getPathInfo() !== '/login_check' || !$request->isMethod('POST')) {
            return;
        }

        // Validate reCAPTCHA response
        $recaptchaResponse = $request->request->get('g-recaptcha-response');
        if (!$this->isRecaptchaValid($recaptchaResponse)) {
            // Store error and redirect back to login page
            $session = $request->getSession();
            $session->set(Security::AUTHENTICATION_ERROR, new AuthenticationException('Invalid reCAPTCHA'));
            $session->set(Security::LAST_USERNAME, $request->request->get('_username'));

            $event->setResponse(new RedirectResponse($this->router->generate('login')));
        }
    }

    private function isRecaptchaValid(string $response): bool
    {
        // Your existing reCAPTCHA validation logic here
        $url = 'https://www.google.com/recaptcha/api/siteverify';
        $data = http_build_query([
            'secret' => $this->recaptchaSecretKey,
            'response' => $response,
            'remoteip' => $request->getClientIp()
        ]);

        $context = stream_context_create([
            'http' => [
                'method' => 'POST',
                'header' => 'Content-Type: application/x-www-form-urlencoded',
                'content' => $data
            ]
        ]);

        $result = json_decode(file_get_contents($url, false, $context), true);
        return $result['success'] ?? false;
    }
}

Step 2: Register the Listener in Services.yml

Make sure to set a high priority so it runs before the default authentication listener:

# app/config/services.yml
services:
    my_bundle.recaptcha_validation_listener:
        class: MyBundle\EventListener\RecaptchaValidationListener
        arguments:
            - '@router'
            - '%your_recaptcha_secret_key%' # Define this parameter in config.yml
        tags:
            - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest, priority: 10 }

Step 3: Add reCAPTCHA to Your Login Form

Update your Twig login template to include the reCAPTCHA widget:

{# templates/security/login.html.twig #}
<form action="{{ path('login_check') }}" method="post">
    <!-- Username & Password Fields -->
    <div>
        <label for="username">Username:</label>
        <input type="text" id="username" name="_username" value="{{ last_username }}">
    </div>
    <div>
        <label for="password">Password:</label>
        <input type="password" id="password" name="_password">
    </div>

    <!-- reCAPTCHA Widget -->
    <div class="g-recaptcha" data-sitekey="%your_recaptcha_site_key%"></div>

    <button type="submit">Login</button>

    <!-- Display Errors -->
    {% if error %}
        <div class="alert alert-danger">{{ error.messageKey|trans(error.messageData, 'security') }}</div>
    {% endif %}
</form>
<script src="https://www.google.com/recaptcha/api.js" async defer></script>

Which Option to Choose?

  • Option 1 is ideal if you want full control over the authentication flow and integrate reCAPTCHA directly into the security layer.
  • Option 2 is simpler and faster to implement, perfect if you just need to add reCAPTCHA validation without modifying core authentication logic.

内容的提问来源于stack exchange,提问作者user1923631

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:07:16