我正在尝试使用Flask-HTTPAuth创建一个具有基本身份验证的两层身份验证系统。我的应用程序有两条路由,一条基本路由/
可供所有登录用户访问,而一条管理员路由/admin
仅可供(以您期望的方式)以管理员身份登录的用户访问。
因此,我决定通过链接装饰器来实现此目的,代码的相关部分如下所示(其中dbops只是处理与数据库对话的名称空间):
@auth.verify_password
def verify_pw(lastname, password):
ln = lastname.lower()
if ln in dbops.list_users():
hashed_pw = dbops.find_hashed_password(ln)
return bcrypt.checkpw(password.encode('utf8'), hashed_pw.encode('utf8'))
return False
def must_be_admin(f):
@wraps(f)
def wrapper(*args, **kwargs):
if dbops.is_admin(auth.username()):
return f(*args, **kwargs)
return "Not authorized."
return wrapper
@core.route("/")
@auth.login_required
def dataentry():
return render_template("dataentry.html")
@core.route("/admin")
@must_be_admin
@auth.login_required
def admin():
return render_template("admin.html")
只要尝试以管理员用户身份登录的任何人首先访问该/
路由,此方法就可以正常工作:提示输入用户名和密码,然后管理员用户可以转到/admin
并执行已登录的管理员任务。
但是,如果管理员用户首次访问/admin
它,则不会给出登录提示。它只是抛出,并且在调试器中四处寻找之后,我确定它auth.username()
正在返回一个空字符串。因此,我的猜测是由于某种原因,没有应用内部装饰器,因此缺少登录提示。
有人知道这里可能会发生什么吗?
我的第一个假设是这是一个简单的错误,因为直到is_admin
检查之后才调用admin装饰器上的内部函数。因此,我尝试修复auth.username()
在检查之前调用函数-并因此使它可用-的问题,如下所示:
def must_be_admin(f):
@wraps(f)
def wrapper(*args, **kwargs):
dummy_to_get_username = f(*args, **kwargs)
if dbops.is_admin(auth.username()):
return dummy_to_get_username
return "Not authorized."
return wrapper
但这只是造成了相同的行为。
我从此先前的SO中看到,建议库作者执行此操作的方法是仅创建两个单独的Flask-HTTPAuth对象。我可以做到的,没问题。但是很明显,我关于装饰器工作方式的思维模型失败了,所以我想独立于获得我想要的功能来解决这个问题。
有时,在不知道装饰器的作用的情况下,很难确定装饰器的正确使用顺序,但是不幸的是,错误的顺序将使应用程序的行为不正确。
对于在视图功能“之前”运行的装饰器,在这种情况下,通常必须按所需的顺序放置装饰器。因此,我认为您的代码将达到您在使用Flask-HTTPAuthlogin_required
之前的期望must_be_admin
:
@core.route("/admin")
@auth.login_required
@must_be_admin
def admin():
return render_template("admin.html")
这样,将首先检查凭据,如果缺少或无效,login_required
将向浏览器返回401错误,这将使登录提示出现。只有在确定凭据有效之后,您才需要评估管理员装饰器。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句