Flask-Login中裝飾器@login_manager.user_loader的作用及原理


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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM