二、Flask筆記-認證登錄

? Flask-Login: 管理已登錄用戶的用戶會話。
? Werkzeug: 計算密碼散列值并進行核對。
? itsdangerous: 生成并核對加密安全令牌。

1. 創建認證藍本:app/auth/__init__.py
from flask import Blueprint
auth = Blueprint('auth', __name__)
from . import views
2. 注冊認證藍本到create_app()工廠:app/__init__.py:
def create_app(config_name): 
    # ...
    from .auth import auth as auth_blueprint 
    app.register_blueprint(auth_blueprint, url_prefix='/auth')
    return app
3. 安裝flask-login插件
pip install flask-login
4. 創建User模型保存用戶登錄信息:app/models.py
from flask_login import UserMixin
from . import db

class User(UserMixin, db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key = True)
    email = db.Column(db.String(64), unique=True, index=True)
    username = db.Column(db.String(64), unique=True, index=True)
    password_hash = db.Column(db.String(128))
    role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))

    def __repr__(self):
        return '<User %r>' % self.username
5. 在User模型中實現密碼散列:app/models.py
from werkzeug.security import generate_password_hash, check_password_hash
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
# ...

class User(UserMixin, db.Model):
    # ...
    @property
    def password(self):
        raise AttributeError('password is not a readable attribute')

    @password.setter
    def password(self, password):
        self.password_hash = generate_password_hash(password)

    def verify_password(self, password):
        return check_password_hash(self.password_hash, password)

說明:要保證數據庫中用戶密碼的安全,關鍵在于不能存儲密碼本身,而要存儲密碼的散列 值。計算密碼散列值的函數接收密碼作為輸入,使用一種或多種加密算法轉換密碼,最終 得到一個和原始密碼沒有關系的字符序列。核對密碼時,密碼散列值可代替原始密碼,因 為計算散列值的函數是可復現的:只要輸入一樣,結果就一樣。

6. 初始化Flask-Login:app/__init__.py
from flask_login import LoginManager

login_manager = LoginManager()
login_manager.session_protection = 'strong'
login_manager.login_view = 'auth.login'

def create_app(config_name): 
    # ...
    login_manager.init_app(app)
    # ...

說明:LoginManager 對象的 session_protection 屬性可以設為 None、'basic' 或 'strong',以提 供不同的安全等級防止用戶會話遭篡改。設為 'strong' 時,Flask-Login 會記錄客戶端 IP 地址和瀏覽器的用戶代理信息,如果發現異動就登出用戶。login_view 屬性設置登錄頁面 的端點。回憶一下,登錄路由在藍本中定義,因此要在前面加上藍本的名字。

7. 設置用戶回調函數:app/model.py
from . import login_manager

@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))

說明:Flask-Login 要求程序實現一個回調函數,使用指定的標識符加載用戶。加載用戶的回調函數接收以 Unicode 字符串形式表示的用戶標識符。如果能找到用戶,這 個函數必須返回用戶對象;否則應該返回 None。

8. 通過Flask-Login提供的login_required裝飾器來增加路由保護, 例如:
from flask_login import login_required

@app.route('/secret')
@login_required
def secret():
    return 'Only authenticated users are allowed!'

說明:如果未認證用戶訪問這個路由,Flask-Login會將這個請求發往登錄頁面。

9. 添加登錄表單:app/auth/forms.py
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField
from wtforms.validators import DataRequired, Length, Email, Regexp, EqualTo
from wtforms import ValidationError

class LoginForm(FlaskForm):
    email = StringField(u'郵箱', validators=[DataRequired(), Length(1, 64), Email()])
    password = PasswordField(u'密碼', validators=[DataRequired()])
    remember_me = BooleanField(u'記住我')
    submit = SubmitField(u'登錄')
10. 在視圖中添加登錄/登出功能:app/templates/base.html
<ul class="nav navbar-nav navbar-right">
    {% if current_user.is_authenticated() %}
        <li><a href="{{ url_for('auth.logout') }}">Sign Out</a></li>
    {% else %}
        <li><a href="{{ url_for('auth.login') }}">Sign In</a></li>
    {% endif %}
</ul>
11. 實現登錄功能:app/auth/views.py
from flask import render_template, redirect, request, url_for, flash 
from flask_login import login_user
from . import auth
from ..models import User
from .forms import LoginForm

@auth.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        user = User.query.filter_by(email=form.email.data).first()
        if user is not None and user.verify_password(form.password.data):
            login_user(user, form.remember_me.data)
            return redirect(request.args.get('next') or url_for('main.index'))
        flash('Invalid username or password.')
    return _render('login', locals())
12. 渲染登錄頁面: app/templates/auth/login.html
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}登錄{% endblock %}
{% block page_content %}
    <div class="page-header">
        <h1>Login</h1>
    </div>
    <div class="col-md-4">
        {{ wtf.quick_form(form) }}
    </div>
{% endblock %}
13. 實現登出功能:app/auth/views.html
@auth.route('/logout')
@login_required
def logout():
    logout_user()
    flash('You have been logged out.')
    return redirect(url_for('main.index'))
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 22年12月更新:個人網站關停,如果仍舊對舊教程有興趣參考 Github 的markdown內容[https://...
    tangyefei閱讀 35,224評論 22 257
  • 大多數程序都需要進行用戶跟蹤。用戶鏈接程序時需要進行身份認證,通過這一過程,讓程序知道自己的身份。程序知道用戶是誰...
    藕絲空間閱讀 1,026評論 0 0
  • 第二部分 Blog例子 第八章 用戶驗證 大部分程序需要追蹤用戶身份。當用戶連接到程序,通過一系列步驟使自己的身份...
    易木成華閱讀 1,322評論 0 4
  • 一個完整的認證系統并不是簡簡單單的字符串相等,還應當考慮到安全性問題,比如用戶的密碼總不能直接暴露在數據庫中吧,萬...
    RickyLin7閱讀 653評論 0 3
  • 最近在學習flask,用到flask-login,發現網上只有0.1版本的中文文檔,看了官方已經0.4了,并且添加...
    ZZES_ZCDC閱讀 5,982評論 3 24