问题
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