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
