Hier eine Implementierung von Google reCAPTCHA in einer Extbase Extension. Als Beispiel dient ein Anmeldeformular (E-Mail, Captcha). Die reCAPTCHA Response wird im Model als Property gehalten, die Property ist via Annotations mit dem Validator verknüpft. Somit kann die Überprüfung des Captcha im Rahmen der Domain Object Validierung erfolgen.

Beim (bzw. kurz vor) Absenden des Formulars wird die reCAPTCHA Response von Google angefordert und diese die entsprechende (hidden) Property des Formulares geschrieben.

Kleiner Schönheitsfehler: Das Secret ist hier fest im Validator verankert, eleganter wäre eine dynamische Variante über Typoscript o. ä. Gleiches gilt für den Sitekey, welcher hier statisch im FLUIDTEMPLATE enthalten ist.

Hinweis: Das ist natürlich nur ein vereinfachtes Beispiel für ein simples Formular. Der Validator ist hier fest im Domänenobjekt verankert. Das kann Probleme bereiten, wenn die Objekte zusätzlich an anderen Stellen (automatisch von Extbase) validiert werden (also nicht nur beim Absenden eines Formulars). In diesem Falle macht es dann Sinn, nur die "normalen" Properties (Title, Email etc.) direkt über das Model zu validieren und einen zusätzlichen Validator für das Formular zu implementieren (Controller Action Argument Validation), innerhalb dessen dann wiederum der ReCaptchaValidator aufgerufen wird.

Classes/Domain/Validator/ReCaptchaValidator.php

<?php

namespace Vendor\Extension\Domain\Validator;

class ReCaptchaValidator extends \TYPO3\CMS\Extbase\Validation\Validator\AbstractValidator {

    /**
     * This validator always needs to be executed even if the given value is empty.
     * See AbstractValidator::validate()
     *
     * @var bool
     */
    protected $acceptsEmptyValues = false;

    /**
     * Checks the google recaptcha response
     *
     * @param mixed $recaptchaResponse The captcha response that should be validated
     * @api
     */
    public function isValid($recaptchaResponse) {

        $json = file_get_contents('https://www.google.com/recaptcha/api/siteverify?secret=THE_VERY_SECRET_SECRET&response=' . $recaptchaResponse);
        $data = json_decode($json);
        if (!$data || !$data->success || $data->success != TRUE) {
            $this->addError('Recaptcha', 1712122017);
        }
    }

}

Classes/Domain/Model/Contact.php

<?php

namespace Vendor\Extension\Domain\Model;

class Contact extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity {

    /**
     * email
     *
     * @var string
     * @validate NotEmpty, EmailAddress
     */
    protected $email = '';

    /**
     * recaptchaResponse
     *
     * @var string
     * @validate \Vendor\Extension\Domain\Validator\ReCaptcha
     */
    protected $recaptchaResponse = '';

    /**
     * Returns the email
     *
     * @return string $email
     */
    function getEmail() {
        return $this->email;
    }

    /**
     * Sets the email
     *
     * @param string $email
     * @return void
     */
    function setEmail($email) {
        $this->email = $email;
    }

    /**
     * Returns the recaptchaResponse
     *
     * @return string $recaptchaResponse
     */
    function getRecaptchaResponse() {
        return $this->recaptchaResponse;
    }

    /**
     * Sets the recaptchaResponse
     *
     * @param string $recaptchaResponse
     * @return void
     */
    function setRecaptchaResponse($recaptchaResponse) {
        $this->recaptchaResponse = $recaptchaResponse;
    }

}

Resources/Private/Templates/Contact/New.html

<f:form pluginName="Contact" controller="Contact" action="create" name="newContact" object="{newContact}" enctype="multipart/form-data" id="newContact">

	<!-- email -->
	<label for="email">
		{f:translate(key: 'tx_myext_domain_model_contact.email')}
	</label>
	<f:form.textfield property="email" id="email" class="form-control" errorClass="alert-danger" />

	<!-- captcha -->
	<label for="captcha">
		{f:translate(key: 'text.contact-new.captcha')}
	</label>
	<f:form.validationResults for="newContact.recaptchaResponse">
		<f:if condition="{validationResults.flattenedErrors}">
			<div class="alert alert-danger">
				{f:translate(key: 'validator.recaptcha')}
			</div>
		</f:if>
	</f:form.validationResults>
	<f:form.hidden property="recaptchaResponse" id="recaptchaResponse" />
	<div class="g-recaptcha" data-sitekey="THE_PRIVATE_SITEKEY"></div>

	<!-- submit -->
	<button class="submit recaptcha" type="submit">
		{f:translate(key: 'text.contact-new.submitbutton')}
	</button>

</f:form>

<script src="https://www.google.com/recaptcha/api.js?hl={f:if(condition: '{data.sys_language_uid} == 0', then: 'en', else: 'de')}" async defer></script>

Resources/Public/Js/Script.js

$(document).ready(function () {

    $('.submit.recaptcha').click(function () {
        $('#recaptchaResponse').val(grecaptcha.getResponse());
    });

});




Kommentare