ReCaptcha Enterprise в формах django без django-recaptcha
Как я могу реализовать новые reCaptcha Enterprise
в django на форме без использования старых django-recaptcha
?
Я удалил django-recaptcha = "==2.0.6"
, которые использовали v2 или v3 reCaptcha не Enterprise (. Но я использовал логику интеграции reCaptcha оттуда).
Для этого:
- install
google-cloud-recaptcha-enterprise = "==1.4.1"
, предоставил учетные данные через строку, которую получил из google-console как key.json и установил ее в env; получили учетные данные
RECAPTCHA_CREDENTIALS_JSON = ast.literal_eval(
os.getenv("RECAPTCHA_CREDENTIALS_JSON", "{}",)
.replace("\r", "\\r")
.replace("\n", "\\n")
)
RECAPTCHA_CREDENTIALS_JSON должен быть представлен в виде строки, как:
RECAPTCHA_CREDENTIALS_JSON="{ "type": "service_account", "project_id":..., "private_key": "..." ...., }"
получил
sitekey
для EnterpriseRECAPTCHA_PUBLIC_KEY
;RECAPTCHA_PUBLIC_KEY = os.getenv("RECAPTCHA_PUBLIC_KEY", "...")
добавлен в INSTALLED_APPS
"django.forms",
;добавлен в настройки
FORM_RENDERER = "django.forms.renderers.TemplatesSetting"
;
В Forms.py:
from google.cloud import recaptchaenterprise_v1
RECAPTCHA_ACTION = "login"
if settings.RECAPTCHA_CREDENTIALS_JSON and settings.RECAPTCHA_PUBLIC_KEY:
client = recaptchaenterprise_v1.RecaptchaEnterpriseServiceClient.from_service_account_info(
settings.RECAPTCHA_CREDENTIALS_JSON
)
else:
client = None
Метод проверки результата для поля captcha:
def create_assessment(token):
"""Create an assessment to analyze the risk of a UI action.
Args:
projectID: GCloud Project ID
recaptchaSiteKey: Site key obtained by registering a domain/app to use recaptcha services.
token: The token obtained from the client on passing the recaptchaSiteKey.
recaptchaAction: Action name corresponding to the token.
Return:
bool: True if successful.
"""
project_id = "....."
recaptcha_site_key = settings.RECAPTCHA_PUBLIC_KEY
recaptcha_action = RECAPTCHA_ACTION
# Set the properties of the event to be tracked.
event = recaptchaenterprise_v1.Event(expected_action=recaptcha_action)
event.site_key = recaptcha_site_key
event.token = token
assessment = recaptchaenterprise_v1.Assessment()
assessment.event = event
project_name = f"projects/{project_id}"
# Build the assessment request.
request = recaptchaenterprise_v1.CreateAssessmentRequest()
request.assessment = assessment
request.parent = project_name
response = client.create_assessment(request)
# Check if the token is valid.
if not response.token_properties.valid:
logger.info(
"The CreateAssessment call failed because the token was "
+ "invalid for for the following reasons: "
+ str(response.token_properties.invalid_reason)
)
return None
return True
Вышеуказанный метод получает token
, который мы должны получить из "виджета". Код виджета:
class ReCaptchaBase(forms.widgets.Widget):
"""
Base widget to be used for Google ReCAPTCHA.
public_key -- String value: can optionally be passed to not make use of the
project wide Google Site Key.
"""
recaptcha_response_name = "g-recaptcha-response"
template_name = "oauth2_provider/widget_v2_checkbox.html"
def value_from_datadict(self, data, files, name):
return data.get(self.recaptcha_response_name, None)
def build_attrs(self, base_attrs, extra_attrs=None):
attrs = super(ReCaptchaBase, self).build_attrs(base_attrs, extra_attrs)
attrs["data-callback"] = base_attrs.get("data-callback", "onloadCallback")
attrs["sitekey"] = base_attrs.get("sitekey", settings.RECAPTCHA_PUBLIC_KEY)
attrs["theme"] = base_attrs.get("theme", "light")
attrs["action"] = base_attrs.get("action", RECAPTCHA_ACTION)
return attrs
value_from_datadict
: помогает мне получитьclient
токен;build_attrs
: атрибуты, которые передаются в шаблон;
Добавьте поле в нашу форму, я переписывал PasswordResetForm. Я добавил его как CharFiled в наш виджет.
class ReCaptchaField(forms.CharField):
widget = ReCaptchaBase()
def validate(self, value):
if not create_assessment(value):
raise forms.ValidationError(
self.error_messages["required"], code="required"
)
return value
Форма:
class CustomPasswordResetForm(PasswordResetForm):
"""Reset password form override"""
def __init__(self, *args, **kwargs):
super(CustomPasswordResetForm, self).__init__(*args, **kwargs)
if not settings.RECAPTCHA_CREDENTIALS_JSON or not settings.RECAPTCHA_PUBLIC_KEY:
self.fields.pop("captcha")
captcha = ReCaptchaField()
__init__
: удалять капчу всякий раз, когда учетные данные не предоставлены
Добавьте captcha block
к форме в основном шаблоне password_reset_form.html
:
{% block captcha %}
<div class="justify-center">
<div class="w-full flex mt-3 justify-center">
{{ form.captcha }}
</div>
<div class="w-full flex mt-3 justify-center">
<p>{{ form.errors.captcha }}</p>
</div>
</div>
{% endblock captcha %}
шаблон для виджета widget_v2_checkbox.html
:
<script src="https://www.google.com/recaptcha/enterprise.js?onload=onloadCallback&render=explicit" async defer></script>
<script type="text/javascript">
// Submit function to be called, after reCAPTCHA was successful.
var onloadCallback = function () {
grecaptcha.enterprise.render('id_captcha', {
'sitekey': '{{ widget.attrs.sitekey }}',
'action': '{{ widget.attrs.action }}',
'theme': '{{ widget.attrs.theme }}',
});
};
</script>
<div class="captcha" id="id_captcha"
{% for name, value in widget.attrs.items %}
{% if value is not False %} {{name}}
{% if value is not True %}="{{ value|stringformat:'s' }}"
{% endif %}{% endif %}
{% endfor %}>
</div>