「Flask實戰」魚書項目實戰六

「Flask實戰」魚書項目實戰六

資源介紹參數
資源類別: Python
如遇問題: 聯繫客服/留言反饋
釋放雙眼,帶上耳機,聽聽看~!

flask魚書項目實戰

編寫login的業務邏輯

首先驗證登入需要確認賬號和密碼是否正確,那麼在前面密碼是加密過後儲存在數據庫中的,所以再讀取之前需要解密,在進行對比,這一系列操作可以使用flask中自帶的check_password_hash來完成

# forms/sql_user.py
from werkzeug.security import generate_password_hash, check_password_hash

·····

#raw為傳入的對比數據
def check_password(self, raw):
    return check_password_hash(self._password, raw)

驗證成功後需要設置使用者的登入憑證,即cookie,設置cookie可以使用flask的外掛flask-login來完成,使用pip install flask-login來安裝外掛,利用下面的login_user來設置cookie
首先,先導入flask-login,並且註冊到app

#app/__init__.py
from flask import Flask
from app.models.sql_book import db
from flask_login import LoginManager

#實例化
login_manager = LoginManager()

def create_app():
    app = Flask(__name__)
    app.config.from_object('app.secure')
    app.config.from_object('app.setting')
    register_blueprint(app)

    db.init_app(app)
    db.create_all(app=app)

    #將login_manager註冊到app上
    login_manager.init_app(app)

    return app

def register_blueprint(app):
    from app.web import web
    app.register_blueprint(web)

然後使用驗證


····· from flask_login import login_user ····· @web.route('/login/', methods=['GET', 'POST']) def login(): form = LoginForm(request.form) if request.method == 'POST' and form.validate(): # 從數據庫中查詢email這條記錄 user = User.query.filter_by(email=form.email.data).first() if user and user.check_password(form.password.data): #設置cookie login_user(user) else: flash('賬號不存在或密碼錯誤') return render_template('auth/login.html', form=form)

訪問127.0.0.1:5000/login/,登入已經註冊過的賬戶,在控制台中可以檢視使用者的cookie信息

訪問權限控制

首先要安裝flask的擴展flask-login使用pip install flask_login來安裝,然後實例化並註冊到app

#app/__init__.py
from flask import Flask
from app.models.sql_book import db
from flask_login import LoginManager

# 實例化
login_manager = LoginManager()

def create_app():
    app = Flask(__name__)
    app.config.from_object('app.secure')
    app.config.from_object('app.setting')
    register_blueprint(app)

    db.init_app(app)
    db.create_all(app=app)

    #註冊到app上
    login_manager.init_app(app)

    return app

def register_blueprint(app):
    from app.web import web
    app.register_blueprint(web)

你用來表示使用者的類需要實現一些方法和屬性

is_authenticated
當使用者通過驗證時,也即提供有效證明時返回 True 。(只有通過驗證的使用者會滿足 login_required 的條件。)
is_active
如果這是一個活動使用者且通過驗證,賬戶也已激活,未被停用,也不符合任何你 的應用拒絕一個賬號的條件,返回 True 。不活動的賬號可能不會登入(當然, 是在沒被強制的情況下)。
is_anonymous
如果是一個匿名使用者,返回 True 。(真實使用者應返回 False 。)
get_id()
返回一個能唯一識別使用者的,並能用於從 user_loader 回調中加載使用者的 unicode 。注意着 必須 是一個 unicode —— 如果 ID 原本是 一個 int 或其它類型,你需要把它轉換為 unicode 。
但是可以通過UserMixin這個類來繼承,並且你必須提供一個 user_loader 回調。這個回調用於從會話中存儲的使用者 ID 重新加載使用者對象。它應該接受一個使用者的 unicode ID 作為參數,並且返回相應的使用者對象。
這個方法可以卸載sql_user.py下,但不是User的類方法

from sqlalchemy import Column, Integer, Float, String, Boolean
from werkzeug.security import generate_password_hash, check_password_hash

from app.models.base import Base, db
from app import login_manager

from flask_login import UserMixin


class User(Base, UserMixin):
    id = Column(Integer, primary_key=True)
    _password = Column('password', String(128), nullable=False)
    nickname = Column(String(24), nullable=False)
    phone_number = Column(String(18), unique=True)
    email = Column(String(50), unique=True, nullable=False)
    confirmed = Column(Boolean, default=False)
    beans = Column(Float, default=0)
    send_counter = Column(Integer, default=0)
    receive_counter = Column(Integer, default=0)
    wx_open_id = Column(String(50))
    wx_name = Column(String(32))

    @property
    def password(self):
        return self._password

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

    def check_password(self, raw):
        return check_password_hash(self._password, raw)

@login_manager.user_loader
def get_user(uid):
    return User.query.get(int(uid))

然後只需要在需要登入才可以訪問的頁面前加上裝飾器即可實現功能,例如在gitf.py下的一個視圖函式

from . import web
#導入login_required模塊
from flask_login import login_required


@web.route('/my/gifts')
@login_required
def my_gifts():
    return 'hello'

##未登錄重定向和登入後重定向
未登錄當然不可能返回一個hello,一般網站的處理是重定向到登入界面,並且登入後再次跳轉到未登錄前的界面。
實現跳轉登入界面

# app/__init__.py
from flask import Flask
from app.models.sql_book import db
from flask_login import LoginManager

login_manager = LoginManager()

def create_app():
    app = Flask(__name__)
    app.config.from_object('app.secure')
    app.config.from_object('app.setting')
    register_blueprint(app)

    db.init_app(app)
    db.create_all(app=app)

    login_manager.init_app(app)
    login_manager.login_view = 'web.login'
    login_manager.login_message = '請先登錄'

    return app

def register_blueprint(app):
    from app.web import web
    app.register_blueprint(web)

實現跳轉到登入前的頁面

@web.route('/login/', methods=['GET', 'POST'])
def login():
    form = LoginForm(request.form)
    if request.method == 'POST' and form.validate():
        user = User.query.filter_by(email=form.email.data).first()
        if user and user.check_password(form.password.data):
            login_user(user)
            # 觀察重定向登入頁面的url,跳轉前的頁面被加載了next參數上,所以只需要拿到這個參數即可
            next_page = request.args.get('next')
            # 判斷是否為空,如果為空跳轉到首頁
            # 這裡的首頁在 web/main.py下,需要返回一個response不然就會報錯。
            if not next_page:
                next_page = url_for('web.index')
            return redirect(next_page)
        else:
            flash('賬號不存在或密碼錯誤')
    return render_template('auth/login.html', form=form)

重定向攻擊

上面的代碼存在安全隱患,因為next的參數可控,存在重定向攻擊的風險,所以我們需要判斷這個next參數
這裡給出簡單判斷,也可以使用白名單來校驗

#將next_page的判斷條件修改
if not next_page or not next_page.startswith('/'):
聲明:本文為原創作品,版權歸作者所有。未經許可,不得轉載或用於任何商業用途。如若本站內容侵犯了原著者的合法權益,可聯繫我們進行處理。

給TA打賞
共{{data.count}}人
人已打賞
0 條回復 A文章作者 M管理員
    暫無討論,說說你的看法吧