Извлечение идентификатора пользователя из токена с помощью декоратора

#python #rest #flask

#python #rest #flask

Вопрос:

Я работаю над приложением flask RESTful, которое использует auth0 для аутентификации и авторизации. Затем я написал декоратор, который проверяет токен и извлекает из него идентификатор пользователя. Моя цель — использовать этот идентификатор, извлеченный из токена, для использования внутри оформленной функции и выдавать исключение, если идентификатор пользователя из токена и из параметра URL не совпадают. Это сделано для того, чтобы пользователи не могли изменять данные другого пользователя с помощью его собственного токена. Я не уверен, что это лучшая практика для приложения RESTful, но, похоже, в моем случае это необходимо.

Тем не менее, я пытаюсь найти наилучший подход к передаче идентификатора пользователя из токена в оформленную функцию:

Что-то вроде этого:

 def authorization():
    def inner(func):

        def wrapper(*args, **kwargs):
            try:
                """
                token validation stuff
                ...
                """
                wrapper.user_id = token_payload['user_id']

            except Exception:
                return {"success": False}, 500

            return func(*args, **kwargs)

        return wrapper
    return inner


@authorization()
@app.route('/test', methods=['GET'])
def some_function():
    return jsonify({
        'success': True,
        'user_id': some_function.user_id
    })
  

Как вы можете видеть, я устанавливаю поле user_id в функцию-оболочку, что, по-видимому, не лучший способ сделать это. Есть ли какой-либо другой подход к этой ситуации? возможно, с использованием ресурсов Flask?

Ответ №1:

  1. Вы можете пропустить один уровень переноса в вашем декораторе, поскольку вы не задаете ему никаких параметров.
  2. Кроме того, я бы просто передал извлеченный идентификатор непосредственно в обернутую функцию вместо установки атрибута.
  3. И, наконец, вам следует добавить самый внутренний параметр auth decorater, поскольку оформленная функция — это то, что вы хотите зарегистрировать с помощью Flask.
  4. Дополнительно: используйте functools.wraps для обновления подписи вашей обернутой функции, чтобы упростить самоанализ и отладку.

Таким образом:

 from functools import wraps

def authorized(func):
    @wraps(func)
    def wrapper(*args, **kw):
        try:
            # token stuff
            user_id = token_payload['user_id']
        except:
            return jsonify({"success": False}), 500
        return func(user_id, *args, **kw)
    return wrapper

@app.route('/test', methods=['GET'])
@authorized
def some_function(user_id):
    return jsonify({
        'success': True,
        'user_id': user_id
    })
  

Теперь каждая функция, которую вы оформляете, @authorized должна иметь user_id в качестве своего первого параметра, и все должно работать так, как вы ожидаете.

Комментарии:

1. Хороший момент. У моего настоящего декоратора есть параметры, и я решил передать идентификатор в kwargs, но если я сделаю это, то каждая оформленная функция должна будет иметь ** kwargs в качестве параметра, даже если мне не нужен этот идентификатор для этой функции.

2. @MatheusHernandes если у вас есть какие-то функции, которым требуется авторизация, но которые вам не нужны user_id , вы могли бы просто сделать это параметром вашего декоратора. Что-то вроде @authorized(pass_id=True) , а затем либо вызывайте func(user_id, ..) , либо просто func(..) в зависимости от этого.

3. могло бы быть… Я нахожусь между этой альтернативой и той, о которой идет речь в вопросе

4. @MatheusHernandes Если вам нужен более структурированный способ сделать это, вы можете использовать before_request обработчик для обработки ваших данных авторизации и поместить user_id в flask.g пространство имен контекста приложения. Затем вы можете зарегистрировать отдельный Blueprint для маршрутов, которым требуется авторизация, который использует before_request препроцессор.