вторник, 29 мая 2012 г.

Авторизация во Flask с помощью Login

Для расширенных возможностей авторизации, во Flask, существует расширение Flask-Login (документация, исходники с примером).

В общих чертах

Расширение Flask-Login предназначено для упрощения управления сессиями пользователей и решает типичные задачи, такие как вход/выход, запоминание сессий на определённый срок.
Что делает это расширение:
  1. Хранит ID залогинившихся пользователей внутри сессии и позволяет достаточно просто управлять их входом/выходом.
  2. Позволяет управлять доступом пользователей к представлениям в зависимости от того, залогинились они или нет.
  3. Реализует функцию "запомнить меня".
Что она не делает, а оставляет на нашей совести:
  1. Не навязывает использование конкретных способов хранения данных о пользователях.
  2. Не ограничивает в выборе способа авторизации и ничего не мешает использовать логин/пароль, OpenID или любой другой способ.
  3. Не берет на себя разграничением прав в зависимости от залогинности пользователя.
  4. Не выполняет регистрацию пользователей и восстановление пароля.

Начало использования

Первое что нужно сделать, для начала работы с расширением, это создать в любом месте программы, экземпляр класса LoginManager:
login_manager = LoginManager()
Класс LoginManager содержит код, который позволяет организовать нюансы работы приложения совместно с этим расширением. Например: как загружать данные пользователя по его ID, куда отправлять пользователей когда они пытаются войти или выйти и т.д.
Когда приложение полностью сконфигурировано, необходимо передать его в LoginManager:
login_manager.setup_app(app)

Как строится работа

На каждого пользователя заводится определённая структура (далее User), в которой кроме прочих, нужных Вам данных о пользователе, должен быть уникальный ID и данные по которым можно авторизовать пользователя (например логин/пароль).
И Flask-Login со своей стороны будет требовать от Вас, во-первых, найти объект User по соответствующему ID, а во-вторых, найти и вернуть объект User по переданным при авторизации данным.
Первое необходимо чтобы найти объект User уже авторизованного пользователя, при его последующих запросах исходя из ID который хранится в его сессии.
Для этого необходимо назначить callback-функцию с помощью декоратора user_loader которая используется расширением для загрузки объекта User по его ID:
@login_manager.user_loader
def load_user(userid):
    return User.get(userid)
Входной параметр один - строка unicode содержащая ID пользователя. Функция должна возвращать None, если ID не существует. В противном случае, если все в порядке и пользователь с там ID существует, нужно вернуть соответствующий ему объект User.
Второй случай происходит на этапе авторизации пользователя, когда Вы имеете некоторые данные (из формы авторизации к примеру) и по ним должны определить, корректны ли они и существует ли пользователь им соответствующий. И в этом случае, для того чтобы Flask-Login поместил нужный ID в сессию пользователя, Вы должны передать ему через функцию login_user объект User. Так же, через в эту функцию можно передать параметр remember со значением True, для реализации функционала «запомнить меня» - чтобы сессия пользователя была восстановлена при следующем входе, без авторизации.
Следующих пример демонстрирует вариант авторизации через логин/пароль. Поиск объекта User осуществляется в функции get_user(login, password).
@app.route("/login", methods=["GET", "POST"])
def login():
  if request.method == "POST":
    login = request.form["login"]
    password = request.form["password"]
    remember_me = request.form["remember"]
    # ищем пользователя по логину и паролю
    # get_user - внутренняя функция, для запроса к БД, например     
    user = get_user(login, password)
    if user:
      # если пользователь с тамим логином и паролем существует -       
      # авторизуем и делаем редирект
      login_user(user, remember=remember_me)
      return redirect(url_for("index"))
  return render_template("login.html")

Ограничение доступа к представлениям

Для ограничения доступа, чтобы к представлению могли обращаться только авторизованные пользователи, используется декоратор login_required. При попытке перейти на URL представления с таким декоратором, неавторизованный пользователь будет отправлен на страницу авторизации.

Завершение сеанса

Для того чтобы завершить сеанс пользователя, существует функция logout_user, обратная по смыслу login_user. После её вызова куки из сессии пользователя будут удалены и он будет считаться неавторизованным. Вот пример использования:
@app.route("/logout")
@login_required
def logout():
    logout_user()
    return redirect(somewhere)

Итог

Таким образом подытожим основные действия которые нужно выполнить для добавления в своё приложение функции авторизации:
  1. Определить механизм хранения и создать набор пользователей, каждый из которых будет иметь уникальный ID.
  2. Написать функцию с декоратором user_loader которая будет ставить в соответствие ID конкретный объект пользователя.
  3. Установить декоратор login_required для представлений которые требуют авторизации.
  4. Для авторизации добавить представление, которое будет вызывать функцию login_user с объектом пользователя если данные для авторизации будут верны.
  5. Добавить представление, для реализации выхода пользователя, которое будет вызывать функцию logout_user.