1. 程式人生 > 實用技巧 >flask-login模組官網內容整理

flask-login模組官網內容整理

Flask-Login

官網連結: https://flask-login.readthedocs.io/en/latest/


簡介

用於處理Flask的使用者會話管理,完成登入,登出,記住使用者會話的常見功能。--低階庫

可以完成的功能:

  1. 將活躍使用者id儲存的session中,方便實現登入,退出
  2. 為檢視函式新增登入 or 登出狀態限制
  3. 實現 記住我 功能
  4. 防止使用者sessions被惡意修改

不支援的功能:

  1. 必須自己實現資料庫連線,資料儲存,使用者登入
  2. 限制其不能使用 OpenIDs 等其他驗證方式
  3. 只能處理 是否登入的判定
  4. 不能處理賬號註冊 賬號恢復

開始使用

配置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 物件,該物件需要實現的屬性與方法

  1. is_active 、is_authenticated 返回False
  2. is_anonymous: return True
  3. 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 如果設定為Truecookie,則每次請求都會重新整理它,這會增加生命週期。像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

當使用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