Flask-Login通过装饰器@login_required来检查访问视图函数的用户是否已登录,没有登录时会跳转到login_manager.login_view = 'auth.login'所注册的登录页。登录时即需调用login_user()函数,而在内部调用了由我们注册的回调函数。
Flask-Login就是通过装饰器,来注册回调函数,当没有sessionID时,通过装饰器指定的函数来读取用户到session中,达到在前端模板中调用当前登录用户current_user的目的,该装饰器就是:
@login_manager.user_loader
其功能类似如下
class Test(): # 在Test中并没有真正的定义callback def feature(self): self.callback() def decorate(self, func): self.callback=func return func test = Test() # 将fun注册为回调函数 @test.decorate def fun(): print(1) # 调用feature将触发回调函数 test.feature() # 1
装饰器user_loader:
def user_loader(self, callback): ''' This sets the callback for reloading a user from the session. The function you set should take a user ID (a ``unicode``) and return a user object, or ``None`` if the user does not exist. :param callback: The callback for retrieving a user object. :type callback: callable ''' self.user_callback = callback return callback
这个装饰器的目的即将回调函数赋给self.user_callback
在login_user函数内部:
user_id = getattr(user, current_app.login_manager.id_attribute)()
session['user_id'] = user_id # 在这里设置用户id
session['_fresh'] = fresh
session['_id'] = current_app.login_manager._session_identifier_generator()
if remember:
session['remember'] = 'set'
if duration is not None:
try:
# equal to timedelta.total_seconds() but works with Python 2.6
session['remember_seconds'] = (duration.microseconds +
(duration.seconds +
duration.days * 24 * 3600) *
10**6) / 10.0**6
except AttributeError:
raise Exception('duration must be a datetime.timedelta, '
'instead got: {0}'.format(duration))
_request_ctx_stack.top.user = user
user_logged_in.send(current_app._get_current_object(), user=_get_user())
调用时将user的属性写入session,并绑定到当前的请求上下文。由于HTTP是无状态的,每次发起新请求时flask会创建一个请求上下文,在分发路由时flask-login根据cookie判断用户并绑定到当前的请求上下文,由于这种绑定关系的存在,那么每次新的请求发生时都需要获取user看一下最后绑定的代码:
def reload_user(self, user=None): ctx = _request_ctx_stack.top if user is None: user_id = session.get('user_id') if user_id is None: ctx.user = self.anonymous_user() else: if self.user_callback is None: raise Exception( "No user_loader has been installed for this " "LoginManager. Add one with the " "'LoginManager.user_loader' decorator.") user = self.user_callback(user_id) if user is None: ctx.user = self.anonymous_user() else: ctx.user = user else: ctx.user = user
这个函数即未获得user时调用的,这时就必须调用回调函数self.user_callback
自己定义的回调函数:
@login_manager.user_loader def load_user(user_id): return User.query.get(int(user_id))
self.user_callback就是装饰器装饰的回调函数load_user(),reload_user的作用就是当user无值时调用该方法获取user并绑定到当前的请求上下文,绑定的意义在于每次当我们使用current_user的时候,会直接从当前上下文中返回。
装饰器login_required:
def login_required(func): @wraps(func) def decorated_view(*args, **kwargs): if current_app.login_manager._login_disabled: return func(*args, **kwargs) elif not current_user.is_authenticated: return current_app.login_manager.unauthorized() return func(*args, **kwargs) return decorated_view