Flask-Login


一、安裝和配置

1、安裝

pip install flask-login

 

2、配置flask-login插件

使用Flask-Login的應用程序中最重要的部分是LoginManager類(登錄管理器),然后對app進行配置。

from flask import Flask
from flask_login import LoginManager


app = Flask(__name__)  # 實例化一個flask對象
app.config["SECRET_KEY"] = '雅蠛蝶'  # 設置session的secret key

login_manager = LoginManager()  # 實例化一個登錄的管理實例
login_manager.init_app(app)

默認情況下,Flask-Login使用 sessions 進行身份驗證。也就是數說你必須在app上設置 secret key,否則Flask會向您顯示一條錯誤消息。

 

3、user_loader 回調函數

user session 記錄的是用戶 ID (user_id),回調函數的作用就是通過 user_id 返回對應的 User 對象。 把返回的user對象存儲到session中。 user_loader 回調函數在 user_id 非法的時候不應該拋出異常,而要返回 None。 沒有這個回調函數的話,Flask-Login 將無法工作。
@login_manager.user_loader
def user_loader(user_id):
    from user.models import User  # 導入User表
    user = User.query.get(int(user_id))
    return user

 

4、User類的實現

使用flask-login插件需要User類的實現以下幾個屬性和方法:

  • is_authenticated:屬性,用來判斷是否是已經授權了,如果通過授權就會返回true
  • is_active: 屬性,判斷是否已經激活
  • is_anonymous: 屬性,判斷是否是匿名用戶
  • get_id(): 方法,返回用戶的唯一標識
當然,這些屬性和方法也可以直接繼承於userMixin的默認方法和屬性,不用自己去實現。
from flask_login import UserMixin


class User(UserMixin,db.Model):  # db是flask-sqlalchemy的實例
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)
    password= db.Column(db.String(128))
    avatar_hash = db.Column(db.String(32))

    def __init__(self,id,username,email):
        self.id = id
        self.username = username
        self.email = email

    def __repr__(self):
        return '<User %r>' % self.username

 

二、flask-login的應用

1、登錄Demo

# 用戶進行身份驗證后,你可以使用該login_user 功能登錄。

from flask import request
from test.models import User  # 導入User類


@app.route('/login', methods=['GET', 'POST'])
def login():
    # Here we use a class of some kind to represent and validate our
    # client-side form data. For example, WTForms is a library that will
    # handle this for us, and we use a custom LoginForm to validate.
    form = LoginForm()
    if form.validate():
        # Login and validate the user.
        # user should be an instance of your `User` class
        username = request.form.get('username')
        password = request.form.get('password')
        user = User.query.filter_by(User.username==username, User.password==password).first()
        login_user(user)  # 登錄
        flask.flash('Logged in successfully.')

        next = flask.request.args.get('next')
        # is_safe_url should check if the url is safe for redirects.
        # See http://flask.pocoo.org/snippets/62/ for an example.
        if not is_safe_url(next):
            return flask.abort(400)

        return flask.redirect(next or flask.url_for('index'))
    return flask.render_template('login.html', form=form)

這里的是登陸的請求操作,通過用戶查詢,判斷是否有權限,然后通過login_user(user),其實也是調用user_loader()把用戶設置到session中。

這里的next參數可能有安全問題而不能直接跳轉,可以考慮使用is_safe_url去過濾。

 

2、使用flask-login登陸后的一些參數和方法

1.  登陸后的用戶,默認提供了current_user的用戶代理方法,在每個模板中都可以直接使用:

{% if current_user.is_authenticated %}
  Hi {{ current_user.name }}!
{% endif %}

 

2. 需要用戶登錄的視圖可以使用 @login_required 來裝飾

@app.route("/settings")
@login_required
def settings():
    pass

 

3. 注銷用戶,logout_user(),這樣會清除cookie和session

@app.route("/logout")
@login_required
def logout():
    logout_user()
    return redirect(somewhere)

 

3、自定義登錄操作

# 設置登陸視圖,用於未授權操作的跳轉(即用戶未登錄時,默認跳轉到哪里)
login_manager.login_view = "auth.login"


# 設置快閃消息,用於提示用戶
login_manager.login_message = _("Please login to access this page.")

# 可以設置None,'basic','strong'以提供不同的安全等級
login_manager.session_protection = "basic"

# 自定義消息類別
login_manager.login_message_category = "info"
"""
就是在登陸頁面的重定向時候,可以攜帶到登陸請求的參數,例如在一個用戶頁面,session過期,這是要跳轉到登陸頁面,那么會把當前的鏈接當參數存放到next里面傳遞到登陸請求中。當然了,這里還可以設置USE_SESSION_FOR_NEXT =True 這樣就把鏈接放session里面了;而這些可攜帶參數鏈接必須在info的路徑下。
"""

 

4、使用Request Loader自定義登錄

有時候你想要不使用cookies來登錄用戶,比如使用頭部值或者作為查詢參數傳遞的api鍵值。 在這些情況下,您應該使用request_loader回調。 這個回調應該和你的user_loader回調一樣,只是它接受Flask請求而不是user_id。
舉個例子,你可以通過路徑的參數或者請求頭里攜帶的Authorzation消息進行驗證用戶:

@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

 

5、匿名用戶

默認情況下,當用戶實際未登錄時,current_user 將設置為 AnonymousUserMixin 對象。它具有以下屬性和方法:

  • is_active並且is_authenticated是 False
  • is_anonymous 是 True
  • get_id() 回報  None

如果您對匿名用戶有自定義要求(例如,他們需要具有權限字段),您可以提供一個可調用的(類或工廠函數),用於創建匿名用戶 LoginManager

login_manager.anonymous_user = MyAnonymousUser

6、記住我(remember)

默認情況下,當用戶關閉瀏覽器時,Flask會話將被刪除,用戶將被注銷。“記住我”可防止用戶在關閉瀏覽器時意外退出。但這不意味着記憶或預填充用戶的用戶名和密碼在登錄表單的用戶注銷之后。

“記住我”功能可能很難實現。但是,Flask-Login使它幾乎透明 - 只需  remember=True 轉到 login_user 呼叫。Cookie將保存在用戶的計算機上,然后Flask-Login將自動從該cookie恢復用戶ID(如果該cookie不在會話中)。cookie過期前的時間可以使用  REMEMBER_COOKIE_DURATION 配置進行設置, 也可以傳遞給它 login_user
cookie是防篡改的,因此如果用戶篡改它(即插入別人的用戶ID代替他們自己的用戶ID),cookie就會被拒絕,就好像它不存在一樣。

該級別的功能將自動處理。但是,你可以(並且應該,如果你的應用程序處理任何類型的敏感數據)提供額外的基礎結構,以提高您記住的cookie的安全性。

 

7、替代Tokens

使用用戶ID作為記憶令牌的值意味着您必須更改用戶的ID以使其登錄會話無效。一種改進方法是使用備用用戶ID而不是用戶ID。例如:

@login_manager.user_loader def load_user(user_id): return User.query.filter_by(alternative_id=user_id).first() 

然后,User類下的 get_id 方法將返回替代ID而不是用戶的主ID:

def get_id(self): return unicode(self.alternative_id) 

這樣,當用戶更改密碼時,您可以自由地將用戶的替代ID更改為新的隨機生成的值,這將確保其舊的身份驗證會話將不再有效。請注意,替代ID仍必須唯一標識用戶...將其視為第二個用戶ID。

 

8、Fresh Logins

當用戶登錄時,他們的會話被標記為“新鮮”,這表示他們實際上在該會話上進行了身份驗證。當他們的會話被銷毀並且他們用“記住我”cookie重新登錄時,它被標記為“非新鮮”login_required 不區分新鮮度,這對大多數頁面來說都很好。但是,更改個人信息等敏感操作應該需要重新登錄(無論如何,更改密碼等操作都需要重新輸入密碼。)

fresh_login_required,除了驗證用戶是否已登錄外,還將確保其登錄是新鮮的如果沒有,它會將它們發送到可以重新輸入憑據的頁面。您可以自定義其行為在相同的方式,你可以自定義login_required,通過設置 LoginManager.refresh_viewneeds_refresh_message以及 needs_refresh_message_category

login_manager.refresh_view = "accounts.reauthenticate"
login_manager.needs_refresh_message = (
    u"To protect your account, please reauthenticate to access this page."
)
login_manager.needs_refresh_message_category = "info"

 

或者通過提供自己的回調來處理刷新

@login_manager.needs_refresh_handler
def refresh():
    # do stuff
    return a_response

要將會話再次標記為新鮮,請調用  flask_login.confirm_login 函數。

 

9、Cookie設置

REMEMBER_COOKIE_NAME

存儲“記住我”信息的 cookie 名。 默認值: remember_token

REMEMBER_COOKIE_DURATION

cookie過期時間,為一個 datetime.timedelta 對象。 默認值: 365 天 (1 非閏陽歷年)

REMEMBER_COOKIE_DOMAIN

如果“記住我” cookie 應跨域,在此處設置域名值 (即 .example.com 會允許 example 下所有子域 名)。 默認值: None

REMEMBER_COOKIE_PATH

限制”記住我“ cookie 存儲到某一路徑下。 默認值: /

REMEMBER_COOKIE_SECURE

 限制 “Remember Me” cookie 在某些安全通道下有用 (典型地 HTTPS)。默認值: None 

REMEMBER_COOKIE_HTTPONLY

保護 “Remember Me” cookie 不能通過客戶端腳本訪問。 默認值: False

REMEMBER_COOKIE_REFRESH_EACH_REQUEST

如果設置為True,cookie則會在每次請求時刷新,這會破壞生命周期,默認值:False

 

10、會話保護

雖然上述功能有助於保護您的“記住我”令牌免受Cookie竊賊的攻擊,但會話Cookie仍然容易受到攻擊。Flask-Login包括會話保護,以幫助防止用戶的會話被盜。

你可以LoginManager在應用程序的配置中配置會話保護如果啟用,它可以在任一 模式basicstrong模式下運行。要將其設置為LoginManager,請將session_protection屬性設置 "basic""strong"

login_manager.session_protection = "strong"

或者,禁用它:

login_manager.session_protection = None

默認情況下,它在"basic"模式下激活它可以在應用程序的配置通過設置被禁用SESSION_PROTECTION設置None, "basic""strong"

當會話保護處於活動狀態時,每個請求都會為用戶的計算機生成一個標識符(基本上是IP地址和用戶代理的安全散列)。如果會話沒有關聯的標識符,則將存儲生成的標識符。如果它有一個標識符,並且它與生成的標識符匹配,那么請求就可以了。

如果標識符在basic模式下不匹配,或者會話是永久性的,則會話將被簡單地標記為非新鮮,並且任何需要重新登錄的內容將強制用戶重新進行身份驗證。(當然,您必須已經在適當的時候使用新的登錄才能產生效果。)

如果標識符在strong非永久會話的模式下不匹配,則刪除整個會話(以及記憶標記,如果存在)。

 

11、禁用API的會話

在對API進行身份驗證時,您可能希望禁用設置Flask會話cookie。為此,請使用自定義會話接口,該接口根據您在請求中設置的標志跳過保存會話。例如:

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

 

12、本地化

默認情況下,LoginManager用於flash在需要用戶登錄時顯示消息。這些消息是英文的。如果您需要本地化,請在發送之前將這些消息localize_callback屬性設置LoginManager為要調用的函數flash,例如gettext將使用消息調用此函數,並將其返回值發送給flash

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM