如何使用 Flask-JWT-Extended 为选定用户保护端点?

How to secure an endpoint for selected users with Flask-JWT-Extended?

假设我要保护一条名为/protected的路由,我将执行以下操作:

@app.route('/protected')
@jwt_required
def protected():
    return "Protected", 200

这样做意味着只有经过身份验证的用户才能访问 /protected。但是,如果我想保护一个名为 /protected/123 的路由,只允许 User123 访问该路由怎么办?

为了激发这个问题,我正在尝试实现一个 edit-profile 功能。当 User123 访问 /users/edit/123 时,服务器将使用现有的用户数据进行响应。当然,我要确保服务器只有在请求来自User123时才会响应。

Flask-JWT-Extended 不提供任何装饰器让您通过用户 ID 限制视图。

您可以在视图中查看用户abort if the userid doesn't match the id in the route. E.g. if you are using the automatic user loading feature, check if the user id matches via the current_user object:

from flask import abort
from flask_jwt_extended import current_user

@app.route('/users/<userid:int>/edit')
@jwt_required
def users_edit(userid):
    if userid != current_user.id:
        abort(403)

    # ... handle view for matching user

注意:我将 URL 更改为将用户 ID 放在 /users/ 之后,这样您就可以与其他 user-related 路由保持一致的 URL。

如果您不使用自动用户加载而是依赖于 JWT 身份声明(sub claim usually), use get_jwt_identity() 并检查该值:

    # assuming that the sub claim is an integer value
    if userid != get_jwt_identity()

您始终可以创建自己的 装饰器,在调用装饰函数之前进行检查:

from functools import wraps
from flask_jwt_extended import current_user, jwt_protected

def userid_must_match(f):
    """Abort with a 403 Forbidden if the userid doesn't match the jwt token

    This decorator adds the @protected decorator

    Checks for a `userid` parameter to the function and aborts with 
    status code 403 if this doesn't match the user identified by the
    token.
    
    """

    @wraps(f)
    @jwt_protected
    def wrapper(*args, userid=None, **kwargs):
        if userid is not None and userid != current_user.id:
            abort(403)
        return f(*args, **kwargs)

    return wrapper

如果路由中没有 userid 参数,装饰器假设不需要进行检查。

这样使用:

@app.route('/users/<userid:int>/edit')
@userid_must_match
def users_edit():
    # ... handle view for matching user

从设计的角度来看,我这样做是为了让您可以省去 用户 ID;这样您就可以访问 /users/edit 并编辑您自己的设置:

from flask import abort
from flask_jwt_extended import current_user

@app.route('/users/edit')
@app.route('/users/<userid:int>/edit')
@userid_must_match
def users_edit():
    # ... handle view for matching user via current_user

如果您想授予对此类帐户进行编辑的任何用户的访问权限,则可以考虑检查管理员或超级用户帐户。

如果您需要更高级的用户管理选项,我还会研究其他 Flask 插件,例如 Flask-Principal to handle roles and permissions, or even Flask-Security