通过Flask中的装饰器进行身份验证 [英] Authentication via decorators in Flask

查看:235
本文介绍了通过Flask中的装饰器进行身份验证的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下有效的代码.它验证给定URL的管理员用户.如果用户不是Admin,则返回401.

代码段1:

__author__ = 'xxxx'

from flask import render_template, url_for
from flask import Blueprint, redirect, request, session, abort
from google.appengine.api import users


admin_routes = Blueprint('admin_routes', __name__)


@admin_routes.route('/xxxx')
def basic():
    user = users.get_current_user()

    if user:
        if not users.is_current_user_admin():
            return abort(401)
        else:
            return render_template('xxx/xxxx.html', user=user, 
                                   logout=users.create_logout_url('/'))
    else:
        return redirect(users.create_login_url('/xxxx'))

上面的代码有效,我想用它做一个装饰器.所以我写了以下内容.但是,它不起作用,因为

的值

用户=无

代码段2:

from google.appengine.api import users
from flask import abort, redirect


def authenticate_admin(func):
    def authenticate_and_call(*args, **kwargs):
        user = users.get_current_user()
        if user is None:
            print("Redirecting user to login page")
            return redirect(users.create_login_url('xxxxx/xxxx'), 401)
        else:
            if not users.is_current_user_admin():
                return abort, 401
            return func(*args, **kwargs)
    return authenticate_and_call()

我将如何编写装饰器,使其完成代码段1的工作.最终结果应该是这样的.

__author__ = 'xxxxxx'

from flask import render_template, url_for
from flask import Blueprint, redirect, request, session, abort
from google.appengine.api import users


admin_routes = Blueprint('admin_routes', __name__)

@authenticate_admin
@admin_routes.route('/xxxx')
def basic():
    return render_template('xxx/xxxx.html', user=user, 
                                   logout=users.create_logout_url('/'))

我在上面的代码中得到的异常是

UndefinedError: 'None' has no attribute 'nickname'

解决方案

装饰器的顺序很重要.如果代码的结构与问题相同,则admin_routes装饰basic并返回一个函数(funcA).然后,该函数funcAauthenticate_admin装饰,该函数返回funcB.因此,实际上分配给路由的回调的函数是给admin_routes的函数,它是basic,而不是装饰版的basic(funcAfuncB).因此,您的funcB不会被调用,因此不会执行您的身份验证逻辑

将订单更改为

@admin_routes.route('/xxxx')
@authenticate_admin
def basic():
    ...

此处authenticate_admin返回修饰的函数funcA,然后由admin_routes修饰.因此,分配为回调的函数是funcA而不是basic.因此,当您转到/xxxxfuncA并执行身份验证逻辑时.

错误似乎是当您未登录时导航至/xxxx时,它尝试使用user=None尝试render_template,并且很可能您的模板使用的是user.nickname,即AttributeError.

I have the following code which works. It authenticates admin users for the given url. If the users is not Admin then it returns a 401.

Snippet 1:

__author__ = 'xxxx'

from flask import render_template, url_for
from flask import Blueprint, redirect, request, session, abort
from google.appengine.api import users


admin_routes = Blueprint('admin_routes', __name__)


@admin_routes.route('/xxxx')
def basic():
    user = users.get_current_user()

    if user:
        if not users.is_current_user_admin():
            return abort(401)
        else:
            return render_template('xxx/xxxx.html', user=user, 
                                   logout=users.create_logout_url('/'))
    else:
        return redirect(users.create_login_url('/xxxx'))

The above code works and I wanted to make a decorator out of it. So I wrote the following. However it does not work, as the value of

user = None

Snippet 2:

from google.appengine.api import users
from flask import abort, redirect


def authenticate_admin(func):
    def authenticate_and_call(*args, **kwargs):
        user = users.get_current_user()
        if user is None:
            print("Redirecting user to login page")
            return redirect(users.create_login_url('xxxxx/xxxx'), 401)
        else:
            if not users.is_current_user_admin():
                return abort, 401
            return func(*args, **kwargs)
    return authenticate_and_call()

How would I write the decorator so it does what the Snippet 1 does. End result should be something like so.

__author__ = 'xxxxxx'

from flask import render_template, url_for
from flask import Blueprint, redirect, request, session, abort
from google.appengine.api import users


admin_routes = Blueprint('admin_routes', __name__)

@authenticate_admin
@admin_routes.route('/xxxx')
def basic():
    return render_template('xxx/xxxx.html', user=user, 
                                   logout=users.create_logout_url('/'))

The exception i get for the above code is

UndefinedError: 'None' has no attribute 'nickname'

解决方案

The order of decorators matter. If the code is structured as in the question, admin_routes decorates basic and returns a function (funcA). That function, funcA, is then decorated by authenticate_admin which returns funcB. So the function that actually is assigned as callback for the route is the function given to admin_routes which is basic and not the decorated version of basic (funcA, or funcB). So your funcB is never called and hence your authentication logic is not executed

When you change the order to

@admin_routes.route('/xxxx')
@authenticate_admin
def basic():
    ...

Here authenticate_admin returns the decorated function funcA which is then decorated by admin_routes. So the function assigned as a callback is funcA not basic. So when you go to /xxxx, funcA and your authentication logic is executed.

The error seems to be when you navigate to /xxxx when you are not logged in, it tries to render_template with user=None and most likely your template uses user.nickname which is an AttributeError.

这篇关于通过Flask中的装饰器进行身份验证的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆