#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:
- Вы можете пропустить один уровень переноса в вашем декораторе, поскольку вы не задаете ему никаких параметров.
- Кроме того, я бы просто передал извлеченный идентификатор непосредственно в обернутую функцию вместо установки атрибута.
- И, наконец, вам следует добавить самый внутренний параметр auth decorater, поскольку оформленная функция — это то, что вы хотите зарегистрировать с помощью Flask.
- Дополнительно: используйте
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
препроцессор.