Тема 6 из 6

Сессии, вход и страницы ошибок

HTTP «забывчив»: каждый запрос сервер обрабатывает с нуля и не знает, кто его прислал. Чтобы помнить посетителя — его корзину, счётчик, вход на сайт, — нужны сессии.

Session

from flask import Flask, session

app = Flask(__name__)
app.secret_key = "длинная-случайная-строка"


@app.route("/")
def index():
    session["visits"] = session.get("visits", 0) + 1
    return f"Вы здесь в {session['visits']}-й раз"

session работает как словарь, но у каждого посетителя он свой. Flask хранит его в cookie браузера и подписывает ключом secret_key: посетитель может cookie прочитать, но подделать — нет.

  • session.get(ключ, по_умолчанию), session[ключ] = значение;
  • session.pop(ключ, None) — удалить ключ, session.clear() — очистить всё;
  • в настоящем проекте secret_key — длинная случайная строка (python -c "import secrets; print(secrets.token_hex())"), и хранят её не в коде, а в переменной окружения.

Глобальные переменные в коде — общие для всех посетителей. В них можно хранить данные сайта (список проектов, сообщения), но не данные конкретного человека.

Вход на сайт

import hmac


@app.route("/login", methods=["POST"])
def login():
    if hmac.compare_digest(request.form.get("password", ""), PASSWORD):
        session["admin"] = True
        return redirect(url_for("admin"))
    return "Неверный пароль", 401


@app.route("/admin")
def admin():
    if session.get("admin") is not True:
        return redirect(url_for("login"))
    return "Панель владельца"
  • hmac.compare_digest сравнивает строки за одинаковое время, и злоумышленник не может по скорости ответа угадывать пароль по символу;
  • проверяйте значение, а не наличие ключа: "admin" in session истинно и для session["admin"] = False;
  • в настоящих проектах пароль хранят не открытым текстом, а в виде хеша (werkzeug.security.generate_password_hash), а для пользователей берут готовые расширения вроде Flask-Login.

Свои страницы ошибок

@app.errorhandler(404)
def not_found(e):
    if request.path.startswith("/api/"):
        return {"error": "not found"}, 404
    return render_template_string("<h1>Страница не найдена</h1>"), 404

Обработчик вызывается для любого неизвестного адреса и для abort(404). Код нужно вернуть явно — иначе ответ уйдёт с кодом 200, и поисковики решат, что страница существует. Так же делаются обработчики для 500 и других кодов.

Что дальше

Готовый сайт выкладывают на хостинг (Render, PythonAnywhere, свой сервер). Там его запускают не через app.run, а через сервер вроде gunicorn или waitress: waitress-serve --port=8000 app:app. Данные хранят в базе — например, SQLite через расширение Flask-SQLAlchemy.

Итог

  • secret_key и session — свои данные у каждого посетителя;
  • вход: hmac.compare_digest, session["admin"] is True, перенаправление гостей;
  • @app.errorhandler(404) — свой ответ, но с правильным кодом;
  • глобальные переменные — общие для всех.