Вездесущий спам — читал где то, что одно время 80% почтового трафика это был SPAM.
Сейчас уже почтового спама не так много доходит до нас (просто средства борьбы с ним стали лучше).
Я столкнулся со спамом в формах обратной связи на сайтах, да и вообще в формах которые на сайтах позволяют писать и отправлять сообщения.
С годами, образовалось большое количество сайтов, и клиенты стали жаловаться на спам, конечно при разработке были сделаны простые защиты (скрытое поле и/или 2+2 и т. д. и т. п.) так же был опыт использования reCAPTCHA v2 — но были свои нюансы.
reCAPTCHA v2 считаю, что это хорошее решение. Да и ещё оно работает из коробки с flask-wtf примерно с 2017 года. Раньше нужно было «колхозить».
Решил написать для себя простой шаблон, ничего лишнего, форма обратной связи в контактах сайта защищенная reCAPTCHA. Мне легче использовать такую выжимку из проекта, чем копаться в большом готовом проекте с кучей библиотек. Здесь нечего лишнего, бери и используй.
Что понадобиться?
- Ключи от google публичный и приватный. (Я создал для хоста 127.0.0.1 возможно для теста подойдут они. А так нужно при создании указывать домен на котором будет сайт с капчей.)
- Flask и flask-wtf.
Структура проекта
.
├── app.py
├── forms.py
├── requirements.txt
└── templates
├── base.html
└── index.html
Пошаговые действия
Создать виртуальное окружение для python, всё как обычно:
python3.7 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
Содержимое requirements.txt
Flask==1.1.2
Flask-WTF==0.14.3
Создаю директорию для шаблонов:
mkdir templates
Создаю файл base.html (в директории templates) следующего вида, строки 8,9 содержат блок который будет расширен содержимым файла index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Flask RecaptchaField simple - {{ title }}</title>
</head>
<body>
{% block content %}
{% endblock content %}
</body>
</html>
Содержимое файла index.html (в директории templates). Строка 1 указывает какой шаблон будет расширен текущим. Строка 5 будет содержать сообщение об успехе или провале валидации. Собственно в 11 строке содержится recaptcha.
{% extends "base.html" %}
{% block content %}
{% if msg %}
Результат: {{ msg }}
{% endif %}
<form action="/" method="post">
{{ form.csrf_token }}
{{ form.text.label }} {{ form.text(size=20) }}
{{ form.recaptcha }}
<input type="submit" value="Отправить">
</form>
{% endblock %}
Следующий файл forms.py (корень проекта). Строка 8 содержит поле комментарий DataRequired обозначает обязательно для заполнения. Строка 9 recaptcha поле с «галочкой».
from flask_wtf import FlaskForm
from flask_wtf import RecaptchaField
from wtforms import TextField
from wtforms.validators import DataRequired
class ContactForm(FlaskForm):
text = TextField('Комментарий', validators=[DataRequired()])
recaptcha = RecaptchaField()
И наконец самый главный файл приложения app.py (корень проекта).
Строка 5 — ранее описанная форма.
Строка 10, 11 — ключи которые нужно получить в google используя аккаунт google.
Строка 16 — форма для передачи в шаблон и проверки валидности.
Строка 18 — если метод POST значит была нажата в форме кнопка submit и соответственно нужно проверить полученные данные на валидность.
Строка 19 — проверка данных на валидность.
Если валидация пройдена, то msg примет значение «Успех!» иначе «Ошибка валидации».
from flask import Flask
from flask import request
from flask import render_template
# форма с валидацией и капчей
from forms import ContactForm
app = Flask(__name__)
app.config['SECRET_KEY'] = "12345"
# ключи recaptcha от google
app.config['RECAPTCHA_PUBLIC_KEY'] = "6Ld74-oUAAAAAJC0UOY6PtrOrNcxQ2VQCfGAqBOC"
app.config['RECAPTCHA_PRIVATE_KEY'] = "6Ld74-oUAAAAAD2_Jl2IVKh2uCCI9OPX_7oTdLz4"
@app.route('/', methods=['GET', 'POST'])
def index():
form = ContactForm()
msg = ""
if request.method == "POST":
if form.validate_on_submit():
msg="Успех!"
# отправить почту, записать в БД
else:
msg="Ошибка валидации"
# обработать ошибку
return render_template("index.html",
title="index page",
form=form,
msg=msg)
if __name__ == '__main__':
app.run(debug=True)
Запуск проекта:
python3 app.py
В видео видно, что если ошибиться несколько раз, будет показана капча и просто «галочкой» уже не отделаться.
В заключении
Я расписал подробно шаги для того, что бы запомнить самому, делать всё на автомате. В будущем буду экономить время и не лениться добавлять валлидацию там где нужно. Это не переводная статья и не куски документации один к одному — это то что я использую в своих проектах.
В новой версии flask-wtf появиться параметр RECAPTCHA_DISABLE который позволит красиво отключать и включать проверку капчи.
А как быть, если нужно отключить проверку капчи сейчас?
Вообще это одна из проблем, почему мне не нравилось использовал данную капчу, решение пока у меня такое, создавать две одинаковые формы с reCAPTCHA и без. Пример кода ниже forms.py и app.py. Выделил строки которые поменялись.
Файл forms.py
from flask_wtf import FlaskForm
from flask_wtf import RecaptchaField
from wtforms import TextField
from wtforms.validators import DataRequired
class ContactForm(FlaskForm):
text = TextField('Комментарий', validators=[DataRequired()])
class ContactRecaptchaForm(ContactForm):
recaptcha = RecaptchaField()
Файл app.py
from flask import Flask
from flask import request
from flask import render_template
app = Flask(__name__)
app.config['SECRET_KEY'] = "12345"
# ключи recaptcha от google
app.config['RECAPTCHA_PUBLIC_KEY'] = "6Ld74-oUAAAAAJC0UOY6PtrOrNcxQ2VQCfGAqBOC"
app.config['RECAPTCHA_PRIVATE_KEY'] = "6Ld74-oUAAAAAD2_Jl2IVKh2uCCI9OPX_7oTdLz4"
app.config['RECAPTCHA_DISABLE'] = True # будет капча или нет
# форма с валидацией и капчей или без неё.
# в новой версии flask-wtf планируется сделать RECAPTCHA_DISABLE красиво
# из коробки, это временное решение
if app.config['RECAPTCHA_DISABLE'] == True:
from forms import ContactRecaptchaForm as ContactForm
else:
from forms import ContactForm
@app.route('/', methods=['GET', 'POST'])
def index():
form = ContactForm()
msg = ""
if request.method == "POST":
if form.validate_on_submit():
msg="Успех!"
# отправить почту, записать в БД
else:
msg="Ошибка валидации"
# обработать ошибку
return render_template("index.html",
title="index page",
form=form,
msg=msg)
if __name__ == '__main__':
app.run(debug=True)
Готовый код с примером Flask + reCAPTCHA v2 Google
Можно скачать его тут.
git clone https://github.com/newivan/flask-simple-captcha