pip install flask-loginhtml
接下來建立擴展對象實例:算法
from flask import Flask from flask_login import LoginManager app = Flask(__name__) login_manager = LoginManager(app)
同時,你能夠對LoginManager對象賦上配置參數:數據庫
# 設置登陸視圖的名稱,若是一個未登陸用戶請求一個只有登陸用戶才能訪問的視圖, # 則閃現一條錯誤消息,並重定向到這裏設置的登陸視圖。 # 若是未設置登陸視圖,則直接返回401錯誤。 login_manager.login_view = 'login' # 設置當未登陸用戶請求一個只有登陸用戶才能訪問的視圖時,閃現的錯誤消息的內容, # 默認的錯誤消息是:Please log in to access this page.。 login_manager.login_message = 'Unauthorized User' # 設置閃現的錯誤消息的類別 login_manager.login_message_category = "info"
使用Flask-Login以前,你須要先定義用戶類,該類必須實現如下三個屬性和一個方法:flask
屬性 is_authenticated安全
當用戶登陸成功後,該屬性爲True。session
屬性 is_activeapp
若是該用戶帳號已被激活,且該用戶已登陸成功,則此屬性爲True。eclipse
屬性 is_anonymous函數
是否爲匿名用戶(未登陸用戶)。ui
方法 get_id()
每一個用戶都必須有一個惟一的標識符做爲ID,該方法能夠返回當前用戶的ID,這裏ID必須是Unicode。
由於每次寫個用戶類很麻煩,Flask-Login提供了」UserMixin」類,你能夠直接繼承它便可:
from flask_login import UserMixin class User(UserMixin): pass
在編寫登陸登出視圖前,咱們要先寫一個加載用戶對象的方法。它的功能是根據傳入的用戶ID,構造一個新的用戶類的對象。爲了簡化範例,咱們不引入數據庫,而是在列表裏定義用戶記錄。
# 用戶記錄表 users = [ {'username': 'Tom', 'password': '111111'}, {'username': 'Michael', 'password': '123456'} ] # 經過用戶名,獲取用戶記錄,若是不存在,則返回None def query_user(username): for user in users: if user['username'] == username: return user # 若是用戶名存在則構建一個新的用戶類對象,並使用用戶名做爲ID # 若是不存在,必須返回None @login_manager.user_loader def load_user(username): if query_user(username) is not None: curr_user = User() curr_user.id = username return curr_user
上述代碼中,經過」@login_manager.user_loader」裝飾器修飾的方法,既是咱們要實現的加載用戶對象方法。它是一個回調函數,在每次請求過來後,Flask-Login都會從Session中尋找」user_id」的值,若是找到的話,就會用這個」user_id」值來調用此回調函數,並構建一個用戶類對象。所以,沒有這個回調的話,Flask-Login將沒法工做。
有一個問題,啓用Session的話必定須要客戶端容許Cookie,由於Session ID是保存在Cookie中的,若是Cookie被禁用了怎麼辦?那咱們的應用只好經過請求參數將用戶信息帶過來,通常狀況下會使用一個動態的Token來表示登陸用戶的信息。此時,咱們就不能依靠」@login_manager.user_loader」回調,而是使用」@login_manager.request_loader」回調。
from flask import request # 從請求參數中獲取Token,若是Token所對應的用戶存在則構建一個新的用戶類對象 # 並使用用戶名做爲ID,若是不存在,必須返回None @login_manager.request_loader def load_user_from_request(request): username = request.args.get('token') if query_user(username) is not None: curr_user = User() curr_user.id = username return curr_user
爲了簡化代碼,上面的例子就直接使用用戶名做爲Token了,實際項目中,你們仍是要用一個複雜的算法來驗證Token。
一切準備就緒,咱們開始實現登陸視圖:
from flask import render_template, redirect, url_for, flash from flask_login import login_user @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': username = request.form.get('username') user = query_user(username) # 驗證表單中提交的用戶名和密碼 if user is not None and request.form['password'] == user['password']: curr_user = User() curr_user.id = username # 經過Flask-Login的login_user方法登陸用戶 login_user(curr_user) # 若是請求中有next參數,則重定向到其指定的地址, # 沒有next參數,則重定向到"index"視圖 next = request.args.get('next') return redirect(next or url_for('index')) flash('Wrong username or password!') # GET 請求 return render_template('login.html')
上述代碼同以前Login視圖最大的不一樣就是你在用戶驗證經過後,須要調用Flask-Login擴展提供的」login_user()」方法來讓用戶登陸,該方法需傳入用戶類對象。這個」login_user()」方法會幫助你操做用戶Session,而且會在請求上下文中記錄用戶信息。另外,在具體實現時,建議你們對」next」參數值做驗證,避免被URL注入攻擊。
「login.html」模板很簡單,就是顯示一個用戶名密碼的表單:
<!doctype html> <title>Login Sample</title> <h1>Login</h1> {% with messages = get_flashed_messages() %} <div>{{ messages[0] }}</div> {% endwith %} <form action="{{ url_for('login') }}" method="POST"> <input type="text" name="username" id="username" placeholder="Username"></input> <input type="password" name="password" id="password" placeholder="Password"></input> <input type="submit" name="submit"></input> </form>
接下來,讓咱們寫個index視圖
from flask_login import current_user, login_required @app.route('/') @login_required def index(): return 'Logged in as: %s' % current_user.get_id()
裝飾器」@login_required」確保只有登陸用戶才能訪問這個index視圖,Flask-Login幫咱們實現了這個裝飾器。若是用戶未登陸,它就會將頁面重定向到登陸視圖,也就是咱們在第一節中配置的」login_manager.login_view」的視圖。
同時,重定向的地址會自動加上」next」參數,參數的值是當前用戶請求的地址,這樣,登陸成功後就會跳轉回當前視圖。能夠看到咱們對於用戶登陸所須要的操做,這個裝飾器基本都實現了
Flask-Login還提供了」current_user」代理,能夠訪問到登陸用戶的用戶類對象。咱們在模板中也可使用這個代理。讓咱們再寫一個home視圖:
@app.route('/home') @login_required def home(): return render_template('hello.html')
模板代碼以下:
<!doctype html> <title>Login Sample</title> {% if current_user.is_authenticated %} <h1>Hello {{ current_user.get_id() }}!</h1> {% endif %}
在上面的模板代碼中,咱們直接訪問了」current_user」對象的屬性和方法。
登出視圖也很簡單,Flask-Login提供了」logout_user()」方法來幫助你清理用戶Session。
from flask_login import logout_user @app.route('/logout') @login_required def logout(): logout_user() return 'Logged out successfully!'
「@login_required」裝飾器對於未登陸用戶訪問的默認處理是重定向到登陸視圖,若是咱們不想它這麼作的話,能夠自定義處理方法:
@login_manager.unauthorized_handler def unauthorized_handler(): return 'Unauthorized'
這個」@login_manager.unauthorized_handler」裝飾器所修飾的方法就會代替」@login_required」裝飾器的默認處理方法。有了上面的代碼,當未登陸用戶訪問index視圖時,頁面就會直接返回」Unauthorized」信息。
在登陸視圖中,調用」login_user()」方法時,傳入」remember=True」參數,便可實現「記住我」功能:
... login_user(curr_user, remember=True) ...
Flask-Login是經過在Cookie實現的,它會在Cookie中添加一個」remember_token」字段來記住以前登陸的用戶信息,因此禁用Cookie的話,該功能將沒法工做。
當用戶經過帳號和密碼登陸後,Flask-Login會將其標識爲Fresh登陸,即在Session中設置」_fresh」字段爲True。而用戶經過Remember Me自動登陸的話,則不標識爲Fresh登陸。對於」@login_required」裝飾器修飾的視圖,是否Fresh登陸均可以訪問,可是有些狀況下,咱們會強制要求用戶登陸一次,好比修改登陸密碼,這時候,咱們能夠用」@fresh_login_required」裝飾器來修飾該視圖。這樣,經過Remember Me自動登陸的用戶,將沒法訪問該視圖:
from flask_login import fresh_login_required @app.route('/home') @fresh_login_required def home(): return 'Logged in as: %s' % current_user.get_id()
Flask-Login自動啓用會話保護功能。對於每一個請求,它會驗證用戶標識,這個標識是由客戶端IP地址和User Agent的值經SHA512編碼而來。在用戶登陸成功時,Flask-Login就會將這個值保存起來以便後續檢查。默認的會話保護模式是」basic」,爲了增強安全性,你能夠啓用強會話保護模式,方法是配置LoginManager實例對象中的」session_protection」屬性:
login_manager.session_protection = "strong"
在」strong」模式下,一旦用戶標識檢查失敗,便會清空所用Session內容,而且Remember Me也失效。而」basic」模式下,只是將登陸標爲非Fresh登陸。你還能夠將」login_manager.session_protection」置爲None來取消會話保護。