Вездесущий спам — читал где то, что одно время 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