flask
flask_ext
python
Login
在開發ASP.NET
的時候,或多或少會有使用者狀態記錄的需求,這時候也許就會利用SESSION
來記錄登入狀態以及權限,如果你使用的是MASTER-DETAIL
的架構的話,你也許會在MASTER
框上寫一個判斷式,在每一次的PAGE_LOAD
的時候就讀一次權限,不考慮權限的情況下,最少也會考慮人員是否為登入狀態,是的話才給看某個頁面。
而flask-login
就是幫Flask開發人員做這件事的一個擴展應用,官方文件開頭說的很清楚了,flask-login
只做登入/登出的狀態記錄而以,搭配裝飾器讓我們快速的稽核使用者登入某頁面的時候狀態是否為已登入,若否,那就導向login頁面要求使用者登入。
要記得,flask-login
會使用到session
,因此務必設置參數SECRET_KEY
,如下:
flask_login.LoginManager
的__init__
中,還是有app
可以接參數,判斷app
是否為None
,若是則執行self.init_app
UserMixin
幫我們記錄了四種用戶狀態:
觀察一下UserMixin
內有什麼乾坤,如下:
實務上我們會在建置使用者的Model
時繼承UserMixin
, 其中get_id
是return self.id
,這個self.id
就是Model
的id
了,這樣就不難明白為什麼要在使用者Model
中繼承該類了。
目前為止非常抽像,在調整實作專案的時候會比較清楚,先有個概念即可。
第二個前置設置為call back function,設置該function的主要用意是讓flask-login
隨時想到就call你,而且還一定call的到,如下:
當然上面那句是玩笑話,可以看下面的延伸閱讀,在下有簡單說明。
範例?是的,這樣子就可以開始使用flask-login
了,不開玩笑,開始吧。
範例來自官方git,看完範例再看下面的了解程式碼。
相關的說明皆書寫於註解,基本上範例很簡單,大概作業如下:
user
)登入驗證帳號密碼,實務上我們會從資料庫中取回該帳號使用者並驗證密碼是否正確user
作為login_user
的參數綁定
後續就可以直接利用current_user
隨處取得目前的使用者相關資訊(依個人實作User類設置而定)、登入狀態以及利用裝飾器@login_request
驗證登入狀況(內含session
與Flask的thread
、request context
相關觀念)。
試著在未登入的情況下先直接連接@login_required
的網頁
http://127.0.0.1/protected
,正確的話會取得錯誤資訊。
接著利用http://127.0.0.1/login
正常的登入,輸入上面設置的帳號密碼,再重新連接http://127.0.0.1/protected
可以看的到我們利用current_user.id
確實取得使用者帳號資訊
flask-login
確實在實作網站上幫了我們不少忙,current_user
並非只能應用於後端,前端jinja2
也可以直接使用該物件,只要利用{{ current_user}}
就可以在前端直接取得使用者訊息,唯一要注意的是current_user
是一個線程物件,因此在寫入資料庫的時候習慣上會跟Ginberg(Flask Web開發作者)一樣使用current_ser._get_current_object().xx
。(這個觀念也是git上請教Ginberg學來的)
不少人對user_loader
感到好奇,我們透過原始碼了解一下:
實作的程式碼如下:
透過裝飾器,我們將load_user
丟到callback
,並且賦值給user_callback
。
接著我們開始追蹤到底是誰用了user_callback
,看到reload_user
,如下:
第2行:ctx = _request_ctx_stack = LocalStack()
這是Flask請求上下文的佇列,在此實作中取最上的指令,簡單的範例如下:
第10行:我們可以觀察一下初始化LoginManager
,在初始化的時候對這個參數是預設為匿名function
而這個AnonymousUserMixin
不同於Usermixin
,你看,它的is_anonymous
是True
,如下:。
第20行:user = self.user_callback(user_id)
,而user_callback=callback
,而callback就call了你設置的return User.get(userid)
,一切就明白了。
最後,這個reload_user
就四處給人call,這部份可以從login_manager.py
來看,從這邊可以看的到,除了透過session
來記錄之外,最重要的是還搭配了request
的上下文(thread
)。
我們如果沒有需要很複雜的狀態管理,其實簡單自己寫一個function也可以達到相同的目地。
login_user
是主要實作過程中會記錄下我們帳號的一個function,在login
驗證完成之後,我們會讓實作的User
物件當參數丟給login
,常用兩個參數是user
與remember
,用途各自如下:
user
: 實作User
類別remember
: 記住我,實務上會在登錄的Form上有一個Bool欄記錄True/False在經過login_user
之後,後面的應用就都可以利用current_user
來取得用戶資訊,原始碼如下:
第5行:那個getattr(user, current\\\_app.login\\\_manager.id_attribute)
,id_attribute
是何方神聖,是ID_ATTRIBUTE
,而ID_ATTRIBUTE
是是get_id
,所以會取得user
的get_id
,這個user
的get_id
就是設置一中的get_id
,這樣就取得user_id
給session
了,如下程式碼:
第6行:利用session['user_id']
來記錄登錄的使用者id
第10行:記住我的功能,透過參數remember
可以方便的實現這功能
第23行:需求上下文中的佇列最上面的那一個的user
就賦值這個user
給它,user_loader
也是一樣的作法,如下:
current_user
如何可以讓我們在login_user(user)
之後就可以以此物件取得目前用戶資訊,如下:
第2行:條件成立的話就執行current_app.login_manager._load_user()
,如下:
而_load_user
又回傳self.reload_user()
跟上面我們所追蹤的結果是一致的。
這部份在下有簡單說明的文章供查閱
我們預計讓這個function以裝飾器來執行,只要session['userid']
沒有東西,我們就將來源導向login
,如下簡單範例:
第8行:利用next帶著request.url在login之後可以繼續回來源網頁去,只是這部份就有隱約在了,如果允許的話,還是需要特別的處理過。
後續記得在login的時候賦值給session['userid'],這樣子就可以做一個簡單的登入檢核了