用戶登錄功能是 Web 系統一個基本功能,是為用戶提供更好服務的基礎,在 Flask 框架中怎麼做用戶登錄功能呢?今天我們學習一下 Flask 的用戶登錄組件 Flask-Login
Python 之所以如此強大和流行,除了本身易于學習和功能豐富之外,最重要的是因為各種類庫和組件,可以說沒有 Python 做不了的事情,隻有不知道的組件。
但是同一個問題領域中的組件或類庫名稱、功能可能近似,版本多而混亂,會給使用者造成了困擾,比如之前講述的 Flask-Bootstrap 和 Bootstrap-Flask ,以及今天要講述的用戶登錄,由于方式多樣,功能相似,所以出現了很多類似的框架,比如 Flask-Login 、Flask-Auth 、Flask-Security 等等
之所以選擇 Flask-Login,是因為它基于 Session,适合做有 UI 交互的用戶登錄,用我們學習了的 Flask 表單做演示,更容易理清用戶登錄的流程
用戶登錄說明Flask-Login 和其他 Flask 組件并沒有太大區别,有必要開始之前了解下用戶登錄的步驟:
依據以上步驟,我們設計一個應用場景,作為實現:
使用 pip 安裝 Flask-Login 組件:
pip install flask-login
如果一切正常,可以将 Flask-Login 模塊引入:
>>> from flask-login import LoginManager
>>>
本次實踐中,會用到 Flask Form 相關功能,請确保已經安裝了 Flask-WTF 組件,詳見 Web 開發 Form
初始化先實例化 login_manager 對象,然後用它來初始化應用:
from flask import Flask
from flask_login import LoginManager
# ...
app = Flask(__name__) # 創建 Flask 應用
app.secret_key = 'abc' # 設置表單交互密鑰
login_manager = LoginManager() # 實例化登錄管理對象
login_manager.init_app(app) # 初始化應用
login_manager.login_view = 'login' # 設置用戶登錄視圖函數 endpoint
要做用戶驗證,需要維護用戶記錄,為了方便演示,使用一個全局列表 USERS 來記錄用戶信息,并且初始化了兩個用戶信息:
from werkzeug.security import generate_password_hash
# ...
USERS = [
{
"id": 1,
"name": 'lily',
"password": generate_password_hash('123')
},
{
"id": 2,
"name": 'tom',
"password": generate_password_hash('123')
}
]
用戶信息隻包含最基本的信息:
基于用戶信息,定義兩方法,用來創建( create_user )和獲取( get_user )用戶信息:
from werkzeug.security import generate_password_hash
import uuid
# ...
def create_user(user_name, password):
"""創建一個用戶"""
user = {
"name": user_name,
"password": generate_password_hash(password),
"id": uuid.uuid4()
}
USERS.append(user)
def get_user(user_name):
"""根據用戶名獲得用戶記錄"""
for user in USERS:
if user.get("name") == user_name:
return user
return None
下面創建一個用戶類,類維護用戶的登錄狀态,是生成 Session 的基礎,Flask-Login 提供了用戶基類 UserMixin,方便定義自己的用戶類,我們定義一個 User:
from flask_login import UserMixin # 引入用戶基類
from werkzeug.security import check_password_hash
# ...
class User(UserMixin):
"""用戶類"""
def __init__(self, user):
self.username = user.get("name")
self.password_hash = user.get("password")
self.id = user.get("id")
def verify_password(self, password):
"""密碼驗證"""
if self.password_hash is None:
return False
return check_password_hash(self.password_hash, password)
def get_id(self):
"""獲取用戶ID"""
return self.id
@staticmethod
def get(user_id):
"""根據用戶ID獲取用戶實體,為 login_user 方法提供支持"""
if not user_id:
return None
for user in USERS:
if user.get('id') == user_id:
return User(user)
return None
有了用戶類,并且實現了 get 方法,就可以實現 login_manager 的 user_loader 回調函數了,user_loader 的作用是根據 Session 信息加載登錄用戶,它根據用戶 ID,返回一個用戶實例:
@login_manager.user_loader # 定義獲取登錄用戶的方法
def load_user(user_id):
return User.get(user_id)
頁面包括後台和展現(可以理解成前台)兩部分
後台根據前面介紹的 Form 相關知識 (參見 Web 開發 Form ),需要定義一個 Form 類,用來設置頁面的元素和規則:
from wtforms import StringField, PasswordField
from wtforms.validators import DataRequired, EqualTo
# ...
class LoginForm(FlaskForm):
"""登錄表單類"""
username = StringField('用戶名', validators=[DataRequired()])
password = PasswordField('密碼', validators=[DataRequired()])
然後定義一個用戶登錄的視圖函數 login:
from flask import render_template, redirect, url_for, Request
from flask_login import login_user
# ...
@app.route('/login/', methods=('GET', 'POST')) # 登錄
def login():
form = LoginForm()
emsg = None
if form.validate_on_submit():
user_name = form.username.data
password = form.password.data
user_info = get_user(user_name) # 從用戶數據中查找用戶記錄
if user_info is None:
emsg = "用戶名或密碼密碼有誤"
else:
user = User(user_info) # 創建用戶實體
if user.verify_password(password): # 校驗密碼
login_user(user) # 創建用戶 Session
return redirect(request.args.get('next') or url_for('index'))
else:
emsg = "用戶名或密碼密碼有誤"
return render_template('login.Html', form=form, emsg=emsg)
分析下視圖函數的邏輯:
在 templates 模闆下創建登錄頁面的模闆 login.html:
{% macro render_field(field) %} <!-- 定義字段宏 -->
<dt>{{ field.label }}:
<dd>{{ field(**kwargs)|safe }}
{% if field.errors %}
<ul class=errors>
{% for error in field.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</dd>
{% endmacro %}
<!-- 登錄表單 -->
<form method="POST">
{{ form.csrf_token }}
{{ render_field(form.username) }}
{{ render_field(form.password) }}
{% if emsg %} <!-- 如果有錯誤信息 則顯示 -->
<h3> {{ emsg }}</h3>
{% endif %}
<input type="submit" value="登錄">
</form>
為了方便演示,将首頁作為需要驗證的頁面,通過驗證将看到登錄者歡迎信息,頁面上還有個登出鍊接
首頁視圖函數 index:
from flask import render_template, url_for
from flask_login import current_user, login_required
# ...
@app.route('/') # 首頁
@login_required # 需要登錄才能訪問
def index():
return render_template('index.html', username=current_user.username)
首頁模闆 index.html:
<h1>歡迎 {{ username }}!</h1>
<a href='{{ url_for('logout')}}'>登出</a>
登出視圖函數 logout:
from flask import redirect, url_for
from flask_login import logout_user
# ...
@app.route('/logout') # 登出
@login_required
def logout():
logout_user()
return redirect(url_for('login'))
終于可以試試了,加上啟動代碼:
if __name__ == '__main__':
app.run(debug=True)
啟動項目,如果一切正常将看到類似的反饋:
python app.py
* Serving Flask app "app" (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: on
* Restarting with stat
* Debugger is active!
* Debugger PIN: 176-611-251
* Running on http://127.0.0.1:5000/ (Press CTRL C to quit)
訪問 localhost:5000,将看到登錄頁,主要浏覽器地址上的 next 查詢參數:
填寫正确的用戶名和密碼,點擊登錄,将進入首頁:
上面的演示了,已存在用戶登錄的情況,不存在用戶需要完成注冊才能登錄。
注冊功能和登錄很類似,頁面上多了密碼确認字段,并且需要驗證兩次輸入的密碼是否一緻,後台邏輯是:如果用戶不存在,且通過檢驗,将用戶數據保存到 USERS 列表中,跳轉到 login 頁面。
關于具體實現這裡不做詳細講解了,本節代碼示例中有實現,可以參考。
如果您來實現注冊功能的話打算怎麼做?歡迎交流
Flask-Login 其他特性上面的實例中使用了一些 Flask-Login 的基本特性,Flask-Login 還提供了一些其他重要特性
記住我記住我,并不是用戶登出之後,再次登錄時自動填寫用戶名和密碼(這是浏覽器的功能),而是在用戶意外退出後(比如關閉浏覽器)不用再次登錄。
如果用戶本地的 cookie 失效了,Flask-Login 會自動将用戶 Session 放入 cookie 中。
開啟方法是将 login_user 方法的命名參數 remember 設置為 True,此功能默認是關閉的
Session 防護Session 信息一般存放在 cookie 中,但是 cookie 不夠安全,容易被竊取其中 Session 信息,僞造用戶登錄系統,幸運的是 Flask-Login 提供了 Session 防護機制,提供有 basic 和 strong 兩種保護等級,通過 login_manager.session_protection 來開關和設置等級,默認等級為 basic,如果設置為 None 将關閉 Session 防護機制。
在保護機制開啟的情況下,每次請求會根據用戶的特征(一般指有用戶IP、浏覽器類型生成的哈希碼)與 Session 中的對比,如果無法匹配則要求用戶重新登錄,在強模式下( strong )一旦匹配失敗會删除登錄者 Session,以消除攻擊者重構 cookie 的可能
Request Loader有時候因為一些原因不想或者無法使用 cookie,可以将 Session 記錄在其他地方,比如 Header 中或者請求參數中,那麼構造用戶 Session 時就需要将 user_loader 替換為 request_loader, request_loader 将 request 作為參數,這樣就可以從請求的任何數據中獲取 Session 信息了
總結本節課程主要通過一個簡單的用戶登錄實例,介紹了 Flask-Login 組件的使用,大體步驟是:引入 Flask-Login 模塊,初始化應用,構造登錄用戶類,設置登錄頁面入口,使用 login_user 創建用戶 Session, 用 user_loader 恢複登錄者,用 logout_user 推出登錄,還有在視圖函數中如何進行用戶驗證等,最後介紹了一些額外的 Flask-Login 特性。
用戶登錄是 Web 應用的一個常用而又複雜的功能,除了今天介紹的 Session 方式之外,還有基于 RESTful 的非狀态的 token 方式,以及第三方認證機制,比如微信、支付寶等,後面會陸續講解,敬請期待。
,更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!