问题
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'
回答1:
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
.
回答2:
I think you need the return value of authenticate_admin
to be the authenticate_and_call
function, not its invocation authenticate_and_call()
:
return authenticate_and_call
来源:https://stackoverflow.com/questions/31756830/authentication-via-decorators-in-flask