本篇博文跟上一篇[Python][flask][flask-wtf]關於flask-wtf中API使用實例教程有莫大的關系。
簡介:Flask-Login 為 Flask 提供了用戶會話管理。它處理了日常的登入,登出並且長時間記住用戶的會話。
直白的講,flask-login包為用戶管理了涉及到用戶登錄相關的緩存(Session)管理。
Posted by Alima | cnblogs.
一.安裝(Install)
PC環境:Windows 7,Python 3.5.2。
PS:此次配置環境階段和上一篇博文中寫的一致,如果看了上一篇博文,安裝階段可以直接跳過。
- 創建wtfdemo虛擬運行環境
用控制台(管理員運行模式)進入(cd)到想要創建工程的路徑下,創建wtfdemo文件夾。
mkdir wtfdemo
進入(cd)wtfdemo文件夾,創建Python虛擬運行環境。
virtualenv flaskr
出現如下字樣,說明虛擬環境創建成功

PS:本次提供第二種創建Python虛擬運行環境的使用方法
virtualenv -p source_file\python.exe target_file
為什么提出第二種創建方法,你會發現,當你的Python Web程序開發多了以后,PC上難免安裝了很多版本的Python運行環境。
舉例:當PC上同時安裝了Python2.7和Python3.5,運行virtualenv flaskr后,建立的Python虛擬運行環境是Python2.7版本的,但是我們的開發環境是Python3.5。
在控制台中輸入一下指令,你就會發現問題。
virtualenv -h
出現下面圖片顯示,默認的virtualenv安裝路徑是Python2.7,也就是默認的安裝的虛擬環境是Python2.7版本。

所以,在這種情況下,請指定你需要的Python版本,並建立虛擬運行環境。
- 安裝flask-wtf庫文件
進入Python虛擬運行環境(在上一篇博文中寫過),執行以下指令。
pip install flask pip install flask-wtf pip install flask-login
pip install flask-sqlalchemy
出現如下圖所示,說明安裝成功。
flask安裝成功。

flask-wtf安裝成功。

flask-login安裝成功。(本次使用flask-wtf庫時需要輔助以該運行庫)

flask-sqlalchemy安裝成功。

至此,環境配置階段結束。
二.flask-Login介紹
它會:
①將活躍用戶的ID儲存在緩存(Session)中,讓登錄和注銷更加簡單。
②讓你對登入(或登出)的用戶限制瀏覽不同的視圖
③處理略棘手的“記住用戶”功能
④幫助保護使用用戶的緩存(Session),以免被惡意盜用
⑤可能與Flask-Principal或其他授權擴展,在以后的工作中進行整合
但是,它不會:
①強制讓你使用一個特定的數據庫或者其他的存儲方法。你可以完全負責你的用戶時如何加載的。
②限制你使用用戶名和密碼,OpenID或是其他的認證方法。
③處理“登入或登出”以外的權限
④處理用戶注冊或者賬戶恢復
總結,flask-Login包只管理用戶登入登出的緩存(Session)管理,而不做過多的超出自己權限的功能。
三.flask-login下的API介紹
由於flask-login中的API比較少,在本文中,盡可能將所有的API功能介紹列在這里。
- LoginManager
class flask_login.LoginManager(app=None, add_context_processor=True)
這個類是用來保存用戶的登錄狀態的,也是flask-login包的主類。
官方:LoginManager的實例是“不”綁定到特定的應用程序的,所以你可以創建LoginManager對象在你的代碼主體上,所以你可以創建應用程序在工廠函數中。
解析:我們首先看LoginManager類的構造函數
__init__(self, app=None, add_context_processor=True)
該構造函數提供了一個缺省的局部變量app,默認值為None.和上文說道的,為“不綁定到特定的應用程序”解釋說明。也就是說,你可以直接定義LoginManager的實例,而不必去綁定到應用程序上才可以實例化。當你在代碼主題中定義了app后,可以隨時綁定到LoginManager上。
該構造函數初始化了一些列變量。
''' LoginManager構造函數
Blog: www.cnblogs.com/alima/ Editor: Alima | cnblog
''' #: 提供了一個游客用戶,該用戶是在沒有登錄狀態下時使用 self.anonymous_user = AnonymousUserMixin #: 提供當用戶需要登錄時的重定向頁面參數 #: (此處也可以是一個絕對路徑) self.login_view = None #: 提供一個藍圖,當用戶需要登錄時的重定向頁面 #: 如果鍵值為空,則默認重定向到login_view下的地址 self.blueprint_login_views = {} #: 這條消息將flash在重定向頁面上 self.login_message = LOGIN_MESSAGE #: login_message的類型,默認LOGIN_MESSAGE_CATEGORY,可自定義 self.login_message_category = LOGIN_MESSAGE_CATEGORY #: 如果需要一個活躍的登錄,使用戶重定向到其他界面,重新驗證登錄 self.refresh_view = None #: 這條消息將flash到用戶重新驗證的界面上 self.needs_refresh_message = REFRESH_MESSAGE #: needs_refresh_message的類型,默認REFRESH_MESSAGE_CATEGORY,可自定義 self.needs_refresh_message_category = REFRESH_MESSAGE_CATEGORY #: 緩存(Session)的保護等級 #: 有三種等級,分別為: 'basic'(默認),'strong','None'(關閉緩存保護功能) self.session_protection = 'basic' #: 如果存在,則用來轉換使用login_message和needs_refresh_message self.localize_callback = None #: 檢索用戶對象回調 self.user_callback = None #: 未經授權的用戶回調(未登錄狀態) self.unauthorized_callback = None #: 未經授權的用戶回調(需要活躍登錄狀態) self.needs_refresh_callback = None #: 默認屬性以檢索用戶的Unicode標識(源碼注釋在flask_login.config文件下) self.id_attribute = ID_ATTRIBUTE #: 用於回調檢索用戶對象。(設置令牌狀態) self.header_callback = None #: 用於回調檢索用戶對象。(設置Flask請求對象狀態) self.request_callback = None #: 如果應用程序(app)為空,則默認創建一個空的LoginManager實例 if app is not None: self.init_app(app, add_context_processor)
API:init_app()
init_app(self, app, add_context_processor=True)
配置的應用程序。這將注冊一個'after_request`調用,並附加這個`LoginManager`把它作為'app.login_manager`。
這里滲透一下,三個flask架構自帶的裝飾器。
before_request :在請求收到之前綁定一個函數做一些事情。
after_request: 每一個請求之后綁定一個函數,如果請求沒有異常。
teardown_request: 每一個請求之后綁定一個函數,即使遇到了異常。
- 重新定制你的用戶類
在之前的章節中,分別定義了不同的用戶類,flask-login包控制登入登出需要如下屬性。
① is_authenticated
當用戶通過驗證時,返回有效憑證True。
② is_active
一個活動用具有1)通過驗證 2)賬戶也已激活 3)未被停用 4)也不符合任何的應用拒絕登入條件,返回True。不活動的賬號無法登入。
③ is_anonyous
如果是一個匿名用戶,返回True,如果是登錄用戶則返回False
④ get_id()
返回一個能唯一識別用戶的,並能用於從user_loader回調中加載用戶的ID,這個ID必須是Unicode。
如果你對你的新定制的用戶類沒有其他的要求,你可以繼承flask_login.mixins下定義的UserMixin作為你的用戶類的父類。
- flask-login下的API詳解
① 配置登錄
API:@user_loader
解析:這個API設置一個回調,用於從緩存中加載用戶登錄信息。你構造的函數需要設置一個user ID並且返回一個用戶實體。如果用戶不存在則返回None。
使用舉例(簡單實現了一個加載用戶的操作,並沒有對入口變量做判斷):
'''
Editor: Alima | cnblog
'''
@login_manager.user_loader def load_user(id): user = User.query.filter_by(id=id).first() return user
API:@request_loader(@header_loader)
解析:設置一個從flask請求加載用戶的回調。你需要給函數一個Flask請求,函數返回用戶實體,用戶不存在則返回None。
使用實例:(來自官方文檔的一段代碼,很有啟迪,這段代碼歡迎探討,博主理解不是很好。
'''
Blog: www.cnblogs.com/alima/
From: Offical Doc ''' @login_manager.request_loader def load_user_from_request(request): # first, try to login using the api_key url arg api_key = request.args.get('api_key') if api_key: user = User.query.filter_by(api_key=api_key).first() if user: return user # next, try to login using Basic Auth api_key = request.headers.get('Authorization') if api_key: api_key = api_key.replace('Basic ', '', 1) try: api_key = base64.b64decode(api_key) except TypeError: pass user = User.query.filter_by(api_key=api_key).first() if user: return user # finally, return None if both methods did not login the user return None
API:anonymous_user
解析:提供了一個游客用戶,該用戶是在沒有登錄狀態下時使用
使用實例:(你可以判斷當前用戶的is_anonymous屬性,是不是True來看是不是隨機用戶)
if current_user.is_anonymous == True
② 未經授權的配置
API: flask_login.login_view
解析:當用戶需要登錄時,將用戶重定向到的界面。
使用舉例(將非法登錄強制重定向到登錄界面):
login_manager.login_view = 'login'
API: flask_login.login_message
解析:當用戶重定向時,flash出的消息。
使用舉例:
login_manager.login_message = 'Please enter your account'
API:@unauthorized_handler
解析:如果你不想用默認的重定向定制你的登錄,你可以在代碼視圖實體中顯示的寫出一個函數體,將@unauthorized_handler裝飾器添加到函數體之上。這樣做之后,當你用@login_required阻止用戶非法登錄,將執行我們新定義的函數體中的內容。
使用舉例(將非法登錄強制重定向到登錄界面):
'''
Editor: Alima | cnblog
'''
@login_manager.unauthorized_handler
def unauthorized_handler(): return redirect('/login')
③需要活躍登錄的配置
API: flask_login.refresh_view
解析:當用戶需要活躍登錄時,將用戶重定向到的界面。
使用舉例(將非法登錄強制重定向到登錄界面):
login_manager.refresh_view = 'login'
API: flask_login.needs_refresh_message
解析:當需要活躍登錄的用戶重定向時,flash出的消息。
使用舉例:
login_manager.needs_refresh_message = 'You need a fresh log in.Please enter your account'
API:@needs_refresh_handler
解析:作用和unauthorized_handler類似,當你需要登錄的用戶是輸入用戶名密碼登錄時,而你又不想使用默認的重定向方法,那么,你可以顯示的定義你自己處理函數。
使用舉例(將非活躍登錄強制重定向到登錄界面,並flash出消息):
'''
Editor: Alima | cnblog
'''
@login_manager.needs_refresh_handler
def refresh(): flash('You should log in!') return logout()
④登錄機制
API:flask_login.current_user
解析:獲取當前緩存中保存的用戶帳戶信息(一個當前用戶的代理)
API:flask_login.login_fresh()
如果當前用戶是活躍登錄,則返回True。
API:flask_login.login_user(user, remember=False, force=False,fresh=True)
解析Ⅰ:登錄用戶。你需要向函數中傳進一個用戶對象。如果用戶'is_active'屬性為'Flase',只有當'force'是'True'時才會允許登錄。當登錄成功后返回'True',當失敗時返回'False'。
這里特別提一下函數參數'fresh',當設置為'False'時,將登陸用戶的緩存(Session)中標志為不是活躍登錄。默認值為'True'
舉例:
@app.route('/post')
@login_required
def post(): pass
解析Ⅱ:如果只有當前時刻你需要要求你的用戶登錄,你可以這樣做:
if not current_user.is_authenticated:
return current_app.login_manager.unauthorized()
實際上,將上邊的代碼添加到你的視圖中去就可以達到要求了。
當單體測試的時候它可以很方便的全局關閉認證。將'LOGIN_DISABLED'設置為True,裝飾器就會被忽略了。
API:flask_login.logout_user()
解析:用戶登出(你不需要通過實際用戶)。這個函數同時會清除remember me中的cookie(如果存在的話)。
作用就是清除一系列的cookie信息。
API:flask_login.confirm_login()
解析:函數將會將當前Sesslion設置為活躍。當從cookie加載之后,Sesson會變成不活躍狀態。
⑤保護視圖
API:flask_login.login_required
解析:如果你將這個裝飾器放在視圖上,它會保證你的當前用戶是登錄狀態,並且在調用實際視圖之前進行認證。
如果當前用戶不是系統認證的登錄狀態,它將調用LoginManager.unauthorized回調。
API:flask_login.fresh_login_required
解析:如果用這個裝飾器放在視圖上,它將確保當前用戶是活躍登錄的。用戶的Session不是從'remember me'的cookie中加載的。
敏感的操作,例如修改密碼或者郵箱,應該用這個裝飾器保護,來阻止cookie被盜取。
如果用戶沒有認證,通常調用'LoginManager.unauthorized'。
如果用戶被認證了,但是緩存不是活躍登陸,它將會'LoginManager.needs_refresh'代替,而且你需要提供一個'LoginManager.refresh_view'。
幾乎所有的flask-login下的API都在本文介紹了一下,其中@request_loader裝飾器,博主理解不是很好,一旦有進展,會更新在文章中。如果你知道@request_loader裝飾器的用法,歡迎私信評論給博主。
以上就是API的介紹,我們下面來看實例。
四.應用實例
本次在前一篇博文的基礎上,我們來展開本篇博文。
首先,我們要介紹model.py類,以便匹配本次的flask-login。
''' File name: model.py Editor: Alima | cnblogs Blog: www.cnblogs.com/alima/ ''' from wtf import db from flask_login import UserMixin class User(db.Model, UserMixin): id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(80), unique=True) password = db.Column(db.String(80), unique=True) def __init__(self, username, password): self.username = username self.password = password def __repr__(self): return '<User %r>' % self.username
根據我們講到的,使用flask-login驗證用戶登錄時需要如下屬性。
① is_authenticated
② is_active
③ is_anonyous
④ get_id()
User繼承了UserMixin類的屬性,方法。下面附加上flask-login包中的UseMixin類的源碼。
''' From Github: maxcountryman/flask-login ''' class UserMixin(object): ''' This provides default implementations for the methods that Flask-Login expects user objects to have. ''' if not PY2: # pragma: no cover # Python 3 implicitly set __hash__ to None if we override __eq__ # We set it back to its default implementation __hash__ = object.__hash__ @property def is_active(self): return True @property def is_authenticated(self): return True @property def is_anonymous(self): return False def get_id(self): try: return text_type(self.id) except AttributeError: raise NotImplementedError('No `id` attribute - override `get_id`') def __eq__(self, other): ''' Checks the equality of two `UserMixin` objects using `get_id`. ''' if isinstance(other, UserMixin): return self.get_id() == other.get_id() return NotImplemented def __ne__(self, other): ''' Checks the inequality of two `UserMixin` objects using `get_id`. ''' equal = self.__eq__(other) if equal is NotImplemented: return NotImplemented return not equal
附加屬性一目了然,如果你想重新定制你的用戶類,那么請重新類中的方法重寫這個方法,如果仍想調用父類UserMixin中的方法,請使用super()語句。
這下,就解釋了之前沒有提到的UserMixin的作用。
下面來解釋上次有很多有疑點的view.py,並在其中加入本文介紹的一些API的使用。
1 ''' 2 File Name: view.py 3 Editor: Alima | cnblogs 4 Blog: www.cnblogs.com/alima/ 5 ''' 6 7 from flask import render_template, flash, redirect, session, url_for, request, g 8 from flask_login import login_user, logout_user, current_user, login_required, AnonymousUserMixin, fresh_login_required, login_fresh 9 from wtf import app, db, lm, User 10 from form import LoginForm, UploadForm 11 12 @app.before_request 13 def before_request(): 14 g.user = current_user 15 16 @lm.user_loader 17 def load_user(id): 18 user = User.query.filter_by(id=id).first() 19 return user 20 21 @app.route('/login', methods=['GET', 'POST']) 22 def login(): 23 if g.user is not None and g.user.is_authenticated: 24 #login_fresh() 25 session['_fresh'] = False 26 return redirect(url_for('index')) 27 if g.user.is_active == True: 28 flash('active is True') 29 else: 30 flash('active is False') 31 form = LoginForm() 32 if form.validate_on_submit(): 33 user = User.query.filter_by(username=form.username.data, password=form.password.data).first() 34 if(user is not None): 35 login_user(user,remember=form.remember_me.data) 36 return redirect(request.args.get("next") or url_for('index')) 37 return render_template('login.html', form=form) 38 39 @app.route('/', methods = ['GET', 'POST']) 40 @app.route('/index', methods=['GET', 'POST']) 41 @login_required 42 def index(): 43 form = UploadForm() 44 45 if form.validate_on_submit(): 46 filename = secure_filename(form.file.data.filename) 47 form.file.data.save('uploads/' + filename) 48 return redirect(url_for('upload')) 49 50 return render_template('upload.html', form=form) 51 52 @app.route('/logout') 53 @login_required 54 def logout(): 55 logout_user() 56 return redirect(url_for('login'))
解釋:
①第14行,current_user是在當前緩存中取出的用戶實體,如果存在用戶登錄的緩存則返回實體,如果Session不存在則返回None。
②第16行,@lm.user_loader,這個裝飾器表示,每次有請求時,都會調用它所裝飾的函數。
Q: user_loader的運行機制是什么呢?
A: 答案在stackoverflow上有一個最好的解釋,附上連接:flask-login: can't understand how it works
解釋一下答案中的最后幾個點,
1)你需要由你自己寫出這段代碼。,檢查用戶名和密碼是否匹配(非請求到數據庫的情況下)。
也就是說,用戶的cookie中可以存在非法的登錄信息,你需要自己寫出代碼校驗是否符合你所用數據庫的格式。
2)如果認證成功,取得用戶ID,然后將用戶實體它傳遞給login_user()。
③第35行,login_user()博主准備放在左后寫,這是flask-Login的精髓,也是很多人不了解的。
④第41行,@login_required裝飾器將驗證所裝飾的函數是否是在用戶登錄狀態下訪問的。
我們在配置文件(wtf.py)中配置如下信息,指定了當用戶非法登錄時重定向的界面和flash出的消息。
lm.login_view = 'login' lm.login_message = 'Please log in!' lm.login_message_category = 'info'
⑤第25行,session['_fresh'] = False沒看懂,請Ctrl+F在本文中搜索,下面有解釋。
讓我們之間訪問http://127.0.0.1:5000/index會發生什么。

看到紅色框之內的信息了么,就是定義的login_message中的信息。同時頁面跳轉到了http://127.0.0.1:5000/login,也就是定義的login_view。
同時,上文還講到使用unauthorized_handler裝飾器,可以定制你自己的非法登陸處理過程。這里我們做一個簡單的使用過程,非法登錄時,頁面將顯示You have not logged in,你可以自己體驗其中的妙處。
@lm.unauthorized_handler def unauthorized_handler(): return 'You have not logged in!'
下面,讓我們新加入一個需要活躍登錄的界面。
Q:什么是活躍登錄(fresh login)?
A:活躍登錄是指當用戶通過鍵入賬戶,並點擊登錄按鈕后的登錄狀態。
而當用戶通過緩存(Session)登錄時,被認為是非活躍登錄狀態。
Q:為什么區分活躍登錄和非活躍登錄?
A:當用戶需要修改用戶信息,密碼或是其他隱私信息的時候,防止你的PC被別人登錄並惡意修改,或是其它人盜用你的緩存達到你不期望的一些惡意操作。
在view.py中添加如下代碼段。
@app.route('/info', methods = ['GET', 'POST']) @fresh_login_required def info(): return 'Edit your infomation'
在wtf.py中配置如下信息。
lm.refresh_view = 'login' lm.needs_refresh_message = 'Please enter your info' lm.needs_refresh_message_category = "refresh_info"
如果想將頁面重定向到login界面,需要做點小的改善,就是修改flask-login源碼
文件在flaskr\Lib\site-packages\flask_login.py。
修改源碼中的needs_refresh()函數。
1 def needs_refresh(self): 2 ''' 3 This is called when the user is logged in, but they need to be 4 reauthenticated because their session is stale. If you register a 5 callback with `needs_refresh_handler`, then it will be called. 6 Otherwise, it will take the following actions: 7 8 - Flash :attr:`LoginManager.needs_refresh_message` to the user. 9 10 - Redirect the user to :attr:`LoginManager.refresh_view`. (The page 11 they were attempting to access will be passed in the ``next`` 12 query string variable, so you can redirect there if present 13 instead of the homepage.) 14 15 If :attr:`LoginManager.refresh_view` is not defined, then it will 16 simply raise a HTTP 401 (Unauthorized) error instead. 17 18 This should be returned from a view or before/after_request function, 19 otherwise the redirect will have no effect. 20 ''' 21 user_needs_refresh.send(current_app._get_current_object()) 22 23 if self.needs_refresh_callback: 24 return self.needs_refresh_callback() 25 26 if not self.refresh_view: 27 abort(401) 28 29 if self.localize_callback is not None: 30 flash(self.localize_callback(self.needs_refresh_message), 31 category=self.needs_refresh_message_category) 32 else: 33 flash(self.needs_refresh_message, 34 category=self.needs_refresh_message_category) 35 36 logout_user() #Edit by Alima | cnblogs 37 return redirect(login_url(self.refresh_view, request.url))
Q:為什么要更改needs_refresh()函數?
A:試想當你進入info界面,info界面重定向到login界面,由於瀏覽器保留了用戶的登錄緩存,又由login界面跳轉到index界面。
最后當你進入info界面的時候,現在不是活躍登錄,你預想的結果是讓用戶登錄,其實不然,你發現你的瀏覽器進入了index界面。
所以你需要重新定制一下needs_refresh()函數,先將當前用戶退出登錄,這樣就會跳轉到login界面了,一個小小的思維陷阱。
在源碼中沒有實現我們想要的效果,那么就修改源碼。
Q:當我看到view.py中,login函數時,有一個session['_fresh'] = False語句看不懂。
A:flask-login包,並沒有在用戶非活躍登錄狀態,將session['_fresh']設置為Flase的地方,所以我們在那里顯示的設置為Flase。
分別用兩種方式訪問http://127.0.0.1:5000/info
①在鍵入用戶賬戶並登錄成功的情況下,在瀏覽器中直接輸入127.0.0.1:5000/index和127.0.0.1:5000/info
②關閉瀏覽器界面,打開一個新的瀏覽器界面再次分別訪問127.0.0.1:5000/index和127.0.0.1:5000/info
結果:
①在鍵入用戶賬戶並登錄成功的情況下,兩個界面都能夠正常顯示。
②關閉瀏覽器界面,打開一個新的瀏覽器界面。可以訪問127.0.0.1:5000/index,但當訪問127.0.0.1:5000/info會跳轉到登錄界面,如下圖所示。

看到紅色的方框中,正是我們定義的needs_refresh_message信息,並且重定向到login界面。
相信讀到這里,你已經能夠使用:
①login_fresh()判斷當前是否是活躍登錄,是則返回True。
②confirm_login()將當前狀態強制轉換為活躍登錄。
最后的最后,我們解釋login_user()函數。
login_user()函數是flask-login的最核心的函數,附上源代碼。
1 def login_user(user, remember=False, force=False, fresh=True): 2 ''' 3 Logs a user in. You should pass the actual user object to this. If the 4 user's `is_active` property is ``False``, they will not be logged in 5 unless `force` is ``True``. 6 7 This will return ``True`` if the log in attempt succeeds, and ``False`` if 8 it fails (i.e. because the user is inactive). 9 10 :param user: The user object to log in. 11 :type user: object 12 :param remember: Whether to remember the user after their session expires. 13 Defaults to ``False``. 14 :type remember: bool 15 :param force: If the user is inactive, setting this to ``True`` will log 16 them in regardless. Defaults to ``False``. 17 :type force: bool 18 :param fresh: setting this to ``False`` will log in the user with a session 19 marked as not "fresh". Defaults to ``True``. 20 :type fresh: bool 21 ''' 22 if not force and not user.is_active: 23 return False 24 25 user_id = getattr(user, current_app.login_manager.id_attribute)() 26 session['user_id'] = user_id 27 session['_fresh'] = fresh 28 session['_id'] = _create_identifier() 29 30 if remember: 31 session['remember'] = 'set' 32 33 _request_ctx_stack.top.user = user 34 user_logged_in.send(current_app._get_current_object(), user=_get_user()) 35 return True
Top One: 不關閉瀏覽器情況下的Remember me功能
Operation: 讀者可以嘗試一下,當我們運行本次實例時。
①當你登錄用戶,但不使用Remember me功能。
②不關閉瀏覽器,打開一個新的頁面,並將剛剛登錄過的界面關閉。
③在新的界面輸入127.0.0.1:5000/login
Explanation:
你會發現,你並沒有進入login界面,而是進入了index界面,你會想我不是沒有使用Remember me功能么?
其實,經常做Web程序的人會有直覺,問題出現在Session上,的確,確實發生在Session上。
以Chrome瀏覽器為例,打開Chrome菜單->設置->顯示高級設置->內容設置(隱私內容)->所有Cookie和數據庫數據(博主將截圖部分cookie和日期做了模糊處理,請見諒)

我們看到這個session的過期時間是在關閉瀏覽器之后,這樣就解釋了上面的現象。
當然,你可以設置cookie失效時間,下面表格是cookie的一些設置。
| REMEMBER_COOKIE_NAME | 存儲"Remember me"信息的Cookie名。默認值:remember_token |
| REMEMBER_COOKIE_DURATION | cookie過期時間,是一個datetime.timedelta對象。默認值:365天 |
| REMEMBER_COOKIE_DOMAIN | 如果"Remember me"cookie需要跨域,在此設置域名值(eg: .example.com會允許example下所有子域名)。默認值:None |
| REMEMBER_COOKIE_PATH | 限制"Remember me"的cookie存儲到某一路徑下。默認值:/ |
| REMEMBER_COOKIE_SECURE | 限制"Remember me"的cookie在某些安全通道下有用(HTTP)。默認值:None |
| REMEMBER_COOKIE_HTTPONLY | 保護"Remember me"的cookie不能通過客戶端腳本訪問。默認值:False |
TOP Two: 關閉瀏覽器后的Remember me功能。
Operation: 讀者可以嘗試一下,使用Remember me功能登錄。
①進入本次實例的登錄界面,輸入模擬用戶,勾選Remember me,點擊登錄按鈕登錄。
②關閉瀏覽器,重新進入本次實例系統
③你會發現你沒有進入預想的index界面,而是重新進入了login界面。
為什么?讓我們好好回憶一下,提示跟login_user()函數和類的初始化有關系。
對,你可以想到,LoginManager類的初始化是在一切操作之前,那么我們的當前用戶設置成了AnonymousUserMixin(游客),那我們不是做了login_user()操作了么。
讓我們回去看一下代碼。
1 @app.route('/login', methods=['GET', 'POST']) 2 def login(): 3 ''' 4 Alima | cnblogs 5 ''' 6 api_key = request.args.get('api_key') 7 if g.user is not None and g.user.is_authenticated: 8 session['_fresh'] = False 9 flash(current_app.login_manager._login_disabled) 10 flash(current_user.is_authenticated) 11 return redirect(url_for('index')) 12 form = LoginForm() 13 if form.validate_on_submit(): 14 flash(form.remember_me.data) 15 user = User.query.filter_by(username=form.username.data, password=form.password.data).first() 16 if(user is not None): 17 login_user(user, form.remember_me.data) 18 return redirect(request.args.get("next") or url_for('index')) 19 else: 20 print(form.errors) 21 return render_template('login.html', form=form)
發現問題了么,這是因為博主之前了解flask-login包不是很深刻,以為@user_loader會使用戶自動load進LoginManager函數,其實不然。
將第8行改為:
login_user(load_user(g.user.id), True, False, False)
Q:為什么不傳入g.user或者current_user?
A:傳入g.user或者current_user,會發出一個maximum recursion depth exceeded in comparison的錯誤信息。
current_user不是一個純正的用戶類,它的返回值是一個Thread Local 對象。(解析:Flask的Context機制)
current_user = LocalProxy(lambda: _get_user())
Topic Three:logout_user()函數功能。
logout_user()函數功能就是將緩存的用戶信息清楚,將Remember me標記位設置為清空狀態。
1 def logout_user(): 2 ''' 3 Logs a user out. (You do not need to pass the actual user.) This will 4 also clean up the remember me cookie if it exists. 5 ''' 6 7 user = _get_user() 8 9 if 'user_id' in session: 10 session.pop('user_id') 11 12 if '_fresh' in session: 13 session.pop('_fresh') 14 15 cookie_name = current_app.config.get('REMEMBER_COOKIE_NAME', COOKIE_NAME) 16 if cookie_name in request.cookies: 17 session['remember'] = 'clear' 18 19 user_logged_out.send(current_app._get_current_object(), user=user) 20 21 current_app.login_manager.reload_user() 22 return True
至此,本篇介紹基本結束,更深入的flask-login包的使用,博主將在以后為大家開設專題深入講解。
如果你對博文中某些觀點,某些思考角度不一樣的,歡迎在Alima的cnblogs下面留言私信,你們的互動是我的動力。
Posted by Alima | cnblogs。
If there is some question Obsession about this blog,welcome to enter www.cnblogs.com/alima/ and seed message to me.
參考:
[1] flask-login Github maxcountryman https://github.com/maxcountryman/flask-login
[2] flask-login Doc maxcountryman https://flask-login.readthedocs.io/en/latest/
PS:
本篇博文撰寫時間比之前的已發表的幾篇博文相比,時間較長。但內容相比之前更充實,解析並修改源碼是一種新的嘗試。
學習新知識本應如此,追求一些細致的東西,會讓一些未知的問題迎刃而解。
如果你喜歡Alima的博文,歡迎Follow Alima的cnblogs的最新動態。
另外,Alima在謀求一個進取的平台發展,一份安心的Offer,歡迎投來橄欖枝,歡迎提出職位需求,請您聯系我。
----------------------------------
| Alima的聯系方式
| QQ Chat: 995816845
| E-mail:xoxo2191@163.com
----------------------------------
*本文為Alima原創,轉載注明格式[轉載][博客園][Alima][關於flask-login中各種API使用實例],並在文首注明本文鏈接,多謝合作。
*非法轉載及非法抄襲博文將依照網絡著作權流程辦理,請尊重作者勞動成果,最終解釋權歸Alima與博客園共同所有,感謝合作。
*關於惡意爬蟲與刪除關於博主信息的原文進行轉載,請您高抬貴手,分享無價,別讓新博主對這一行失去興趣,營造良好的互聯網環境。
Power by Alima | cnblogs。
