在Web應用中,用戶認證(確認用戶身份)和權限控制(根據用戶角色限制操作)是核心功能。Flask作爲輕量級Web框架,通過Flask-Login擴展可以輕鬆實現這些功能。本文將用最簡單的步驟,帶初學者理解如何用Flask-Login實現用戶登錄、認證狀態保持和基礎權限控制。
一、準備工作:安裝必要庫¶
首先,需要安裝Flask和Flask-Login。如果需要存儲用戶數據,還需安裝Flask-SQLAlchemy(數據庫ORM)和Werkzeug(處理密碼哈希)。打開終端執行:
pip install flask flask-login flask-sqlalchemy werkzeug
二、核心概念:Flask-Login的作用¶
Flask-Login主要解決兩個問題:
1. 會話管理:自動維護用戶登錄狀態(如記住用戶、自動登出超時)。
2. 權限驗證:通過裝飾器控制路由訪問權限(如僅登錄用戶可訪問)。
三、步驟1:配置應用與用戶模型¶
1. 初始化應用和數據庫¶
創建app.py,先初始化Flask應用和數據庫:
from flask import Flask, render_template, redirect, url_for, request, flash
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
from flask_sqlalchemy import SQLAlchemy
from werkzeug.security import generate_password_hash, check_password_hash
# 初始化Flask應用
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key-here' # 用於會話加密,生產環境需換爲隨機字符串
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.db' # 配置SQLite數據庫
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False # 關閉不必要的修改跟蹤
# 初始化數據庫和LoginManager
db = SQLAlchemy(app)
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login' # 未登錄時跳轉的路由(如登錄頁)
2. 定義用戶模型¶
創建User類,繼承UserMixin(自動實現Flask-Login所需的默認方法),並存儲用戶信息:
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True) # 用戶ID
username = db.Column(db.String(80), unique=True, nullable=False) # 用戶名
password_hash = db.Column(db.String(120), nullable=False) # 密碼哈希(非明文)
# 密碼加密方法(存儲時用)
def set_password(self, password):
self.password_hash = generate_password_hash(password)
# 密碼驗證方法(登錄時用)
def check_password(self, password):
return check_password_hash(self.password_hash, password)
3. 創建數據庫表¶
在終端執行python進入交互模式,創建數據庫表:
>>> from app import app, db
>>> with app.app_context():
... db.create_all() # 創建所有表結構
四、步驟2:配置用戶加載與認證¶
1. 配置用戶加載函數¶
Flask-Login需要一個函數從會話中加載用戶。用@login_manager.user_loader裝飾器註冊:
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id)) # 根據用戶ID查詢用戶對象
- 作用:用戶登錄後,Flask-Login會通過這個函數從數據庫加載用戶信息到會話中。
五、步驟3:實現登錄與登出功能¶
1. 登錄頁面與表單¶
創建簡單的登錄頁面(login.html),包含用戶名和密碼輸入框:
<!-- templates/login.html -->
<h1>登錄</h1>
{% if error %}
<p style="color: red;">{{ error }}</p>
{% endif %}
<form method="POST">
<input type="text" name="username" placeholder="用戶名" required><br><br>
<input type="password" name="password" placeholder="密碼" required><br><br>
<button type="submit">登錄</button>
</form>
2. 登錄路由¶
在app.py中添加登錄邏輯:
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
user = User.query.filter_by(username=username).first()
# 驗證用戶是否存在且密碼正確
if user and user.check_password(password):
login_user(user) # 保持登錄狀態(自動生成會話)
return redirect(url_for('dashboard')) # 登錄成功跳轉首頁
else:
return render_template('login.html', error='用戶名或密碼錯誤')
return render_template('login.html') # GET請求顯示登錄表單
3. 登出路由¶
用logout_user()清除會話:
@app.route('/logout')
@login_required # 僅登錄用戶可訪問登出
def logout():
logout_user() # 登出用戶,清除會話
return redirect(url_for('login')) # 重定向到登錄頁
六、步驟4:權限控制¶
1. 保護路由(僅登錄用戶可訪問)¶
用@login_required裝飾器限制路由訪問:
@app.route('/dashboard')
@login_required # 未登錄用戶會被重定向到login_view(即/login)
def dashboard():
return f"歡迎回來,{current_user.username}!" # current_user是當前登錄用戶對象
2. 角色權限控制(進階)¶
如果需要更細粒度的權限(如管理員vs普通用戶),可在用戶模型中添加角色字段:
class User(UserMixin, db.Model):
# ... 其他字段 ...
role = db.Column(db.String(20), default='user') # 角色:user或admin
# 限制管理員路由
@app.route('/admin')
@login_required
def admin_panel():
if current_user.role != 'admin': # 檢查用戶角色
flash('你沒有管理員權限!')
return redirect(url_for('dashboard'))
return "管理員後臺"
七、關鍵注意事項¶
- 密碼安全:必須用
Werkzeug的generate_password_hash加密存儲密碼,絕對禁止明文存儲! - 會話安全:
SECRET_KEY需隨機且複雜,生產環境建議通過環境變量設置。 - 用戶加載:
@login_manager.user_loader是核心,必須確保返回正確的用戶對象。 - 權限驗證:
current_user對象可直接獲取用戶信息,用於動態控制頁面內容。
八、總結¶
通過Flask-Login,我們實現了:
- 用戶登錄狀態的自動維護(會話管理)。
- 路由權限控制(@login_required)。
- 基礎角色權限驗證(通過用戶角色字段)。
後續可擴展方向:添加“記住我”功能、密碼重置、第三方登錄(如OAuth)等。
完整代碼示例¶
以下是整合後的app.py和login.html核心代碼:
app.py
from flask import Flask, render_template, redirect, url_for, request, flash
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
from flask_sqlalchemy import SQLAlchemy
from werkzeug.security import generate_password_hash, check_password_hash
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
login_manager = LoginManager(app)
login_manager.login_view = 'login'
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
password_hash = db.Column(db.String(120), nullable=False)
role = db.Column(db.String(20), default='user')
def set_password(self, password):
self.password_hash = generate_password_hash(password)
def check_password(self, password):
return check_password_hash(self.password_hash, password)
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
user = User.query.filter_by(username=username).first()
if user and user.check_password(password):
login_user(user)
return redirect(url_for('dashboard'))
else:
flash('用戶名或密碼錯誤')
return render_template('login.html')
@app.route('/logout')
@login_required
def logout():
logout_user()
return redirect(url_for('login'))
@app.route('/dashboard')
@login_required
def dashboard():
return f"歡迎,{current_user.username}!"
@app.route('/admin')
@login_required
def admin():
if current_user.role != 'admin':
flash('無權限訪問')
return redirect(url_for('dashboard'))
return "管理員頁面"
if __name__ == '__main__':
with app.app_context():
db.create_all() # 確保第一次運行時創建表
app.run(debug=True)
templates/login.html
<!DOCTYPE html>
<html>
<head><title>登錄</title></head>
<body>
<h1>用戶登錄</h1>
{% with messages = get_flashed_messages() %}
{% if messages %}
<p style="color: red;">{{ messages[0] }}</p>
{% endif %}
{% endwith %}
<form method="POST">
<input type="text" name="username" placeholder="用戶名" required><br><br>
<input type="password" name="password" placeholder="密碼" required><br><br>
<button type="submit">登錄</button>
</form>
</body>
</html>
運行python app.py,訪問http://localhost:5000/login即可測試登錄功能!