Python декораторы и кастомная авторизация на Flask
Для начала про декораторы и зачем они мне вообще понадобились
Как-то давно искал просто и наглядный пример как организовать авторизацию у себя на проекте. Подавляющее большинство примеров били или сложными, или плодили большое количество магии на проекте. Мне же хотелось управлять процессом авторизации.
Среди всего того бардака нашелся пример с декоратором, в статье предлагали проверять наличие куки в запросе и если она присутствовала искать ее уже в соответствующей модели.
Декоратор - это возможность расширения функционала имеющейся функции(объекта), подробней тут wiki
Простой пример
Для начала про сам декоратор, простой пример:
def strong(handler): def wrap(): return '<strong>' + handler() + '</stron>' return wrap
И выведем избитое Hello world
@strong def hello(): return 'Hello world' print(hello()) # <strong>Hello world</strong>
По сути, декоратор - это функция обертка над другой функцией.
Авторизация на Flask
Простой и самый топорный вариант
from app.models.UserModel import UserModel from flask import abort from flask import request def authenticate(func): @wraps(func) def wrapper(*args, **kwargs): token = request.cookies.get('sid') if UserModel.query.filter_by(token=token).first(): return func(*args, **kwargs) abort(401) return wrapper
Проверяем, если есть кука sid и существует запись с таким же пользователем, отдаем страницу по заданому роуту, если нет, делаем abort(401)
@authenticate @app.route('/create-article') def create_article_handler(): ...
Это простейший вариант работы с авторизацией, поверх него можно накрутить много разного. Я например используя flask_restful сделал промежуточный метод синглтон для получения авторизованного пользователя как на примере ниже:
from flask_restful import Resource class Base(Resource) _user = False authenticated = False @property def current_user(self): if self._user is False: token = request.cookies.get('sid') uid = app_redis.get(token) if uid: self._user = UserModel.query.filter_by( id=self.to_int(uid) ).first() return self._user
Модифицируем наш декоратор:
def authenticate(func): @wraps(func) def wrapper(*args, **kwargs): _self = args[0] if len(args) else None user = False if not getattr(func, 'authenticated', True): return func(*args, **kwargs) if hasattr(_self, 'current_user'): user = _self.current_user if user: return func(*args, **kwargs) abort(401) return wrapper
И собственно пример использования:
class ArticleController(Base) @authenticate def put(self) ...
Все довольно просто и что самое главное, управляемо.