flask-login模組官網內容整理
Flask-Login
官網連結: https://flask-login.readthedocs.io/en/latest/
簡介
用於處理Flask的使用者會話管理,完成登入,登出,記住使用者會話的常見功能。--低階庫
可以完成的功能:
- 將活躍使用者id儲存的session中,方便實現登入,退出
- 為檢視函式新增登入 or 登出狀態限制
- 實現 記住我 功能
- 防止使用者sessions被惡意修改
不支援的功能:
- 必須自己實現資料庫連線,資料儲存,使用者登入
- 限制其不能使用 OpenIDs 等其他驗證方式
- 只能處理 是否登入的判定
- 不能處理賬號註冊 賬號恢復
開始使用
配置LoginManager
from flask import Flask
from flask-login import LoginManager
app = Flask(__name__)
app.config["SECRET_KEY"] = "BENNY"
# LoginManager 使用sessions來進行身份驗證,所以在配置檔案中設定 secret_key
login_manager = LoginManager()
login_manager.init_app(app)
如何工作
1, 必須實現 可調的 user_loader, 用於根據使用者id獲取使用者資訊,並存入session內。
# 在定義User表的檔案中實現該函式
@login_manager.user_loader
def load_user(user_id):
return User.get(user_id)
2, User 類定義要求
# 必須實現下面方法 is_authenticated:當用戶通過了身份認證後,會返回True;只有滿足該驗證的使用者才能訪問 login_required修飾的檢視函式 is_active: 活躍賬戶,返回True; 被暫停、禁用的賬號返回False is_anonymous: 匿名使用者返回True;註冊登入使用者返回True get_id: 該方法必須返回一個唯一標識的使用者unicode,可用於從 user_loader返回該使用者;該ID必須是unicode,如果是int 或者其他型別,需要轉化為 unicode # 為了實現這樣的使用者類更方便,可以繼承 UserMixin; 該類提供了這些屬性與方法的預設實現
example:
class User(UserMixin, db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), unique=True, index=True)
email = db.Column(db.String(64), unique=True, index=True)
# Flask-Login integration
def is_authenticated(self):
return True
def is_active(self):
return True
def is_anonymous(self):
return False
def get_id(self):
return self.id
def __repr__(self):
return '<User %r>' % self.username
def __str__(self):
return self.username
登入例項
需要先驗證 next引數的值, 否則,容易受到開啟 重定向的影響
@app.route("/login", methods=["GET","POST"])
def login():
form = LoginForm()
if form.validate_on_submit():
login_manager.login_user(user)
flask.flash("Logged in seccessfully")
next = flask.request.args.get("next")
if not is_sage_url(next):
return flask.abort(404)
return falsk.redirect(next or flask.url_for("idnex"))
return flask.render_template("login.html", form=from)
在模板中使用 current_user 代理訪問登入使用者;該代理在每個模板中都可以使用
{% if current_user.is_authenticated %}
Hi {{ current_user.name }}!
{% endif %}
使用 login_required 裝飾器來限定使用者登入訪問的檢視
@app.route("/settings")
@login_required
def settings():
pass
# 登出登入,需要清空會話中的所有Cookie
@app.route("/logout")
@login_required
def logout():
logout_user()
return redirect(somewhere)
自定義登入過程
預設情況下,當未登入使用者訪問 login_required 裝飾的頁面的時候, flask-login 將flash一條訊息並將其重定向到登入檢視(如果沒有指定登入檢視,將返回401錯誤)
# 繫結登入檢視
login_manager.login_view = "users.login"
# 自定義預設閃爍的訊息
login_manager.login_messages = u"該頁面需要登入後才能訪問"
# 自定義訊息級別
login_manager.login_message_category = "info"
# 如果想進一步自定義流程,可以使用下面的裝飾器功能
@login_manager.unauthorized_handler
def unauthorized():
return a_response
使用請求載入器自定義登入
不適用Cookie來登入使用者,而是使用 header values 或者 api key 作為引數來完成登入,這需要實現 request_loader 回撥函式。
@login_manager.request_loader
def load_user_from_request(request):
# 首先,嘗試從url裡提取api_key
api_key = request.args.get("api_key")
if api_key:
user = User.query.filter_by(api_key=api_key).first()
if user:
return user
# 從請求頭中獲取引數 Authorization
api_key = request.headers.get("Authorization")
if api_key:
api_key = api_key.replace("Basic", "", 1)
try:
api_key = base654.b64decode(api_key)
except TyperError:
pass
user = user.query.filter_by(api_key=api_key).first()
if user:
return user
return None
匿名使用者
未登入使用者需要設定為 AnonymousUserMixin 物件,該物件需要實現的屬性與方法
- is_active 、is_authenticated 返回False
- is_anonymous: return True
- get_id: return None
自定義匿名使用者的類
class AnonymousUser(AnonymousUserMixin):
def can(self, permissions):
return False
def is_administrator(self):
return False
login_manager.anonymous_user = AnonymousUser
remember Me 功能
只需要將 remember=True 傳給login_user 呼叫即可。
通過配置檔案設定Cookie過期時間。
REMEMBER_COOKIE_DURATION
Alternative Tokens 備選tokens
當使用使用者id作為token,意味著必須改變使用者ID才能使其登入session 無效。一個改進的方法是,通過使用一個替代ID。
該替代ID也必須是唯一的,作為第二個使用者ID。
該方法優點: 當用戶修改密碼後,可以直接更新該替代ID,此時,可以保證舊的ID的登入會話無效(已經查詢不到該ID)
@login_manager.user_loader
def load_user(user_id):
return User.query.filter_by(alternative_id=user_id).first()
def get_id(self):
return unicode(self.alternative_id)
修改密碼,重新登入功能(todo ??)
Cookie設定
REMEMBER_COOKIE_NAME |
儲存“記住我”資訊的cookie的名稱。預設值: remember_token |
---|---|
REMEMBER_COOKIE_DURATION |
Cookie過期之前的時間,以datetime.timedelta 物件或整數秒為單位。 預設值: 365天(非non歷公曆年) |
REMEMBER_COOKIE_DOMAIN |
如果“記住我” cookie應該跨域,請在此處設定域值(即.example.com 允許cookie在的所有子域上使用example.com )。 預設: None |
REMEMBER_COOKIE_PATH |
將“記住我” cookie限制為特定路徑。 預設: / |
REMEMBER_COOKIE_SECURE |
將“記住我” cookie的範圍限制為安全通道(通常為HTTPS)。 預設: None |
REMEMBER_COOKIE_HTTPONLY |
防止客戶端指令碼訪問“記住我” cookie。 預設: False |
REMEMBER_COOKIE_REFRESH_EACH_REQUEST |
如果設定為True cookie,則每次請求都會重新整理它,這會增加生命週期。像Flask一樣SESSION_REFRESH_EACH_REQUEST 。 預設: False |
session 保護
保護session避免被修改。可以設定的保護級別為 None, Basic , Strong。設定的路徑,config檔案級別優先於login_manager
# 預設情況下,basic被啟用
logign_manager.session_protection = "strong"
# 設定None,可以禁用該功能
login_manager.session_protection = None
當使用APIs時候,禁用Session Cookie
當使用authentication去驗證API的時候,需要去禁用Flask Sesion Cookie。為了實現這個,需要自定義custom session interface,使用一個標識去跳過會話儲存功能。
from flask import g
from flask.sessions import SecureCookieSessionInterface
from flask_login import user_loaded_from_header
class CustomSessionInterface(SecureCookieSessionInterface):
"""Prevent creating session from API requests."""
def save_session(self, *args, **kwargs):
if g.get('login_via_header'):
return
return super(CustomSessionInterface, self).save_session(*args,
**kwargs)
app.session_interface = CustomSessionInterface()
@user_loaded_from_header.connect
def user_loaded_from_header(self, user=None):
g.login_via_header = True