如何使用参数在Flask中创建python装饰器函数(用于授权)

Mit*_*ops 24 python decorator flask

我在烧瓶登录中使用了一个烧瓶片段来检查用户是否已登录:

from functools import wraps

def logged_in(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if session.get('logged_in') is not None:
            return f(*args, **kwargs)
        else:
            flash('Please log in first.', 'error')
            return redirect(url_for('login'))
    return decorated_function
Run Code Online (Sandbox Code Playgroud)

我这样装饰视图:

@app.route('/secrets', methods=['GET', 'POST'])
@logged_in
def secrets():
    error = None
Run Code Online (Sandbox Code Playgroud)

我也想为授权做类似的事情.现在,我有很多视图来检查用户是否拥有资源,让我们说hotdogs资源.

如果logged_in用户是该特定热狗的所有者,他可以编辑和管理他的热狗.如果他不是,我会把他踢到未经授权的屏幕.

@app.route('/<hotdog>/addmustard/',methods=["GET"])
@logged_in
def addmustard(hotdog):
    if not (authorizeowner(hotdog)):
        return redirect(url_for('unauthorized'))
    do_stuff()
Run Code Online (Sandbox Code Playgroud)

authorizeowner() 将热狗作为输入并检查记录的热狗所有者是否与会话变量中列出的所有者名称匹配.

我尝试制作一个类似于我登录的owns_hotdog包装器/装饰器功能,但它抱怨它不接受参数.我怎样才能达到类似的效果?就像是...

def owns_hotdog(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if not authorizeowner(hotdog):
            return f(*args, **kwargs)
        else:
            flash('Please log in first.', 'error')
            return redirect(url_for('login'))
    return decorated_function
Run Code Online (Sandbox Code Playgroud)

从错误消息中,装饰器似乎没有收到Flask视图可以从路径中的变量访问的热狗参数.我的希望是......

@app.route('/<hotdog>/addmustard/',methods=["GET"])
@logged_in
@owns_hotdog(hotdog)
def addmustard(hotdog):
    do_stuff()
Run Code Online (Sandbox Code Playgroud)

一切都适用于我当前的authorizeowner(热狗)功能,但是将它作为我的路线顶部的包装器,而不是作为路线内的第一行,这似乎更清晰.

其他一些说明:

  • 我知道Flask-Security和Flask-Principal可以为我管理授权.不幸的是,我使用的是不受支持的数据库后端,我无法使用这些扩展.所以,我不得不在没有他们的情况下进行身份验证.
  • 如果你看到以这种方式授权的任何明显漏洞,请告诉我!

Aud*_*kas 20

这是怎么做的:

from functools import update_wrapper

def owns_hotdog(hotdog):
    def decorator(fn):
        def wrapped_function(*args, **kwargs):
            # First check if user is authenticated.
            if not logged_in():
                return redirect(url_for('login'))
            # For authorization error it is better to return status code 403
            # and handle it in errorhandler separately, because the user could
            # be already authenticated, but lack the privileges.
            if not authorizeowner(hotdog):
                abort(403)
            return fn(*args, **kwargs)
        return update_wrapper(wrapped_function, fn)
    return decorator

@app.errorhandler(403)
def forbidden_403(exception):
    return 'No hotdogs for you!', 403
Run Code Online (Sandbox Code Playgroud)

当decorator接受参数时,它不是真正的装饰器,而是返回真正装饰器的工厂函数.

但如果我是你,我会使用Flask-Login进行身份验证,并使用自定义装饰器和函数来扩充它以处理授权.

我看了看Flask-Principal,但发现它的味道过于复杂.没有检查Flask-Security,但我相信它使用Flask-Principal进行授权.总的来说,我认为使用一些自定义代码的Flask-Login足够大部分时间.