繁体   English   中英

如何在 Flask 中实现需要登录的装饰器

[英]How to implement login required decorator in Flask

我有 2 个协同工作的 Flask 应用程序(不同的项目)。 一个实现了一些使用令牌进行身份验证的 API。 第二个使用 API 并为其创建一个 Web 界面。 现在我有一个登录功能,可以将用户名和密码发送到 API,如果正确,则获取身份验证令牌作为回报。 获得令牌后,我将其保存到用户的会话中,现在应将用户视为已登录/已验证。 对于这种情况,我该如何实现 login_required 装饰器。

这是我的登录功能 -

 def login(self):
        response = make_request(BASE_URL + 'login/', clean_data(self.data))
        if response.status_code == 200:
            session['auth_token'] = response.json().get('auth_token')
            return True
        return False

如何制作 login_required 装饰器?

如果重要的话,我还使用 Redis 来存储会话。

查看有关装饰器的官方烧瓶文档: https ://flask.palletsprojects.com/en/1.1.x/patterns/viewdecorators/ 或 python 文档https://www.python.org/dev/peps/pep -0318/还有。

您的装饰器应该类似于:

from functools import wraps
from flask import abort
import jwt

def authorize(f):
    @wraps(f)
    def decorated_function(*args, **kws):
            if not 'Authorization' in request.headers:
               abort(401)

            user = None
            data = request.headers['Authorization'].encode('ascii','ignore')
            token = str.replace(str(data), 'Bearer ','')
            try:
                user = jwt.decode(token, JWT_SECRET, algorithms=['HS256'])['sub']
            except:
                abort(401)

            return f(user, *args, **kws)            
    return decorated_function

...然后在你的 app.py 中你可能有:

@app.route('/api/game', methods=['POST'])
@authorize
def create(user):
    data = json.loads(request.data)
    ....

在这种特殊情况下,我使用 JWT 作为令牌,您的令牌可以分别不同,令牌的解码可以是您的自定义实现,但基本机制与上面的示例非常相似。

我会将以下装饰器函数放在常见的地方

def validate_api_token(validation_func):
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kws):
            api_token = request.headers.get('Authorization')
            is_valid_api_token = validation_func(api_token)
            if is_valid_api_token:
                return f(*args, **kws)

            return 'Invalid API Token', 401

        return decorated_function

    return decorator

对于小型 POC flask 应用程序,如果您可以将令牌存储在非版本控制文件中,则可以使用以下方法:

# tokens are read from a non-versioned `.tokens` file and loaded into a set
api_tokens = load_api_tokens() 


def simple_api_token_validation(api_token):
    return api_token in api_tokens


@app.route("/v1/my/secret/function", methods=['POST'])
@validate_api_token(simple_api_token_validation)
def my_secret_function():
   body = request.get_json()
   # ...

另一个简单的选择是查询数据库(例如 redis):

redis_session = Redis(host=REDIS_HOST, password=REDIS_PASSWORD)


def redis_api_token_validation(api_token):
    if not api_token:
        return False

    api_token_hash = hashlib.sha256(api_token.encode()).hexdigest()
    return redis_session.exists(f'api:tokens:{api_token_hash}')


@app.route("/v1/my/secret/function", methods=['POST'])
@validate_api_token(redis_api_token_validation)
def my_secret_function():
   body = request.get_json()
   # ...

@Velin 回答的最佳 IMO 是使用 jwt 来验证令牌

鉴于每个后续请求都将包含 API 令牌,装饰器应执行以下操作

  • 接受通用请求。 您可以为此使用 *args 和 **kargs
  • 从 header 中提取 token 并将其与存储在 db 中的 token 进行比较(不是 Redis,而是生成的 token 存储在后端的任何地方)
  • 如果通过身份验证,*args 和 **kargs 应该传递给装饰函数
  • 装饰函数的输出应该按原样返回
  • 如果身份验证失败,则应返回一条错误消息。

有关装饰器的解释,请查看此链接: http ://thecodeship.com/patterns/guide-to-python-function-decorators/

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM