Flask-Grundlegende Inhalte-Fünf

I八孝、一個綜上小結(jié)的實例--轉(zhuǎn)賬demo

需求:用戶通過username = admin插掂,password = 1234登錄轉(zhuǎn)賬界面灰瞻,點擊轉(zhuǎn)賬按鈕后提示轉(zhuǎn)賬xxx元到xxx賬戶成功
分析:此處我們應用wtf和cookie來進行驗證用戶登錄,然后用兩個視圖函數(shù)模擬轉(zhuǎn)賬

首先辅甥,根據(jù)創(chuàng)建一個app-transferdemo酝润,根據(jù)以往的經(jīng)驗我們先導入幾個包

from flask import Flask, request, render_template, redirect, url_for, flash, make_response
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, EqualTo

app = Flask(__name__)


if __name__ == '__main__':
    app.run(port=5003, debug=True)

其中make_response相當于Django內(nèi)的Httpresponse

繼而,根據(jù)wtf的驗證方式璃弄,我們需要定義一個登陸驗證表單

class RegisterForm(FlaskForm):
    username = StringField("用戶名:", validators=[DataRequired("請輸入用戶名")], render_kw={"placeholder": "請輸入用戶名"})
    password = PasswordField("密碼", validators=[DataRequired("請輸入密碼"),render_kw={"placeholder": "請輸入密碼"}])
    submit = SubmitField("登陸")

編寫相應的登錄界面logintrans.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="" method="post">
    {{ form.username.label }}{{ form.username }}<br>
    {{ form.password.label }}{{ form.password }}<br>
    {{ form.submit }}<br>
    {{ form.csrf_token }}
</form>
{% for message in get_flashed_messages() %}
    {{ message }}
{% endfor %}
</body>
</html>

編寫登錄驗證的視圖函數(shù)index

@app.route('/', methods=["get", "post"])
def index():
    register_form = RegisterForm()
    if register_form.validate_on_submit():
        # 如果代碼走到if里面證明表單驗證有效
        username = request.form.get("username")
        password = request.form.get("password", "")
        if username == 'admin' and password == '1234':
            return redirect(url_for('transfer'))
    else:
        if request.method == 'POST':
            flash("表單參數(shù)有誤或者不完整")

    return render_template("logintrans.html", form=register_form)

在登錄后要销,跳轉(zhuǎn)到transfer頁面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>轉(zhuǎn)賬頁面</title>
</head>
<body>
<form action="" method="post">
    <label>賬戶</label><input type="text" name="account">
    <label>金額</label><input type="number" name="money">
    <input type="submit" value="轉(zhuǎn)賬">
</form>
</body>
</html>

編寫相應的transfer視圖函數(shù)

@app.route('/transfer',methods=["get", "post"])
def transfer():
    # 取出cookie確保登錄
    username = request.cookies.get('username', '')
    if not username:
        # 無cookie說明未登錄,需要登錄
        return redirect(url_for('index'))
    if request.method == 'POST':
        account = request.form.get('account')
        money = request.form.get('money')
        return '轉(zhuǎn)賬{}元到{}賬戶成功'.format(account, money)
    # makeresponse 相當于Django內(nèi)的Httpresponse
    response = make_response(render_template('transfer.html'))

    return response

啟動服務夏块,觀察效果


II疏咐、CSRF攻擊:跨站請求偽造

首先我們創(chuàng)建兩個應用weba、webb脐供,基于轉(zhuǎn)賬功能浑塞,此處我們不應用wtf驗證
weba.py

from flask import Flask, request, render_template, redirect, url_for, flash, make_response
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, EqualTo

app = Flask(__name__)


# 自定義一個表單類
class RegisterForm(FlaskForm):
    username = StringField("用戶名:", validators=[DataRequired("請輸入用戶名")], render_kw={"placeholder": "請輸入用戶名"})
    password = PasswordField("密碼", validators=[DataRequired("請輸入密碼")])
    password2 = PasswordField("確認密碼", validators=[DataRequired("請輸入確認密碼"), EqualTo("password", "兩次密碼不一致")])
    submit = SubmitField("注冊")


app.secret_key = 'dasdasdsadas'


@app.route('/', methods=["get", "post"])
def index():
    if request.method == 'POST':
        # 取出表單數(shù)據(jù)
        username = request.form.get("username")
        password = request.form.get("password", "")
        password2 = request.form.get("password2", "")
        if not all([username, password]):
            print('參數(shù)error')
        else:
            print(password, username)
            if username == 'admin' and password == '1234':
                # 登錄成功跳轉(zhuǎn)轉(zhuǎn)賬頁面
                response = redirect(url_for('transfer'))
                response.set_cookie('username', username)
                return response
            else:
                print('password error')
    return render_template("login2.html")


@app.route('/transfer',methods=["get", "post"])
def transfer():
    # 取出cookie確保登錄
    username = request.cookies.get('username', '')
    if not username:
        # 無cookie說明未登錄,需要登錄
        return redirect(url_for('index'))
    if request.method == 'POST':
        account = request.form.get('account')
        money = request.form.get('money')
        return '轉(zhuǎn)賬{}元到{}賬戶成功'.format(account, money)
    # makeresponse 相當于Django內(nèi)的Httpresponse
    response = make_response(render_template('transfer.html')
                             )
    return response


if __name__ == '__main__':
    app.run(port=5003, debug=True)

login2.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="" method="post">
    <label>用戶名</label><input type="text" name="username">
    <label>密碼</label><input type="password" name="password">
    <input type="submit" value="登錄">
</form>
</body>
</html>

webb.py

from flask import Flask, render_template

app = Flask(__name__)


@app.route('/', methods=["get", "post"])
def index():
    return render_template('csrf-index.html')


if __name__ == '__main__':
    app.run(port=5004, debug=True)

csrf-index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>襲擊網(wǎng)站A</title>
</head>
<body>
<form action="" method="post">
    <input type="hidden" name="account" value="999">
    <input type="hidden" name="money" value="111">
    <input type="submit" value='點擊領取優(yōu)惠券'>
</form>
</body>
</html>

在建立好如上應用后政己,我們嘗試模擬csrf攻擊酌壕,由webb攻擊weba
由于weba是一個登錄后的轉(zhuǎn)賬頁面,為了攻擊該轉(zhuǎn)賬過程匹颤,嘗試修改csrf-index.html內(nèi)formaction

<form action="http://127.0.0.1:5003/transfer" method="post">

啟動兩個app仅孩,清除網(wǎng)站的cookie,然后點擊點擊領取優(yōu)惠券該按鈕查看過程印蓖。

我們可以發(fā)現(xiàn)辽慕,當admin未登錄時,點擊按鈕返回登錄界面赦肃。當用戶admin登錄后溅蛉,我們點擊按鈕時公浪,會提示轉(zhuǎn)賬信息,而該轉(zhuǎn)賬信息并未來自于admin用戶船侧,而如何防治這種攻擊呢欠气?
我們可以利用csrf_token的方式進行校驗
首先我們編輯生成csrf_token的方法csrf_g.py

import base64
import os


def generate_csrf():
    return bytes.decode(base64.b64encode(os.urandom(48)))


print(generate_csrf())

在每次轉(zhuǎn)賬前都生成一個csrf_token使得每次生成的csrf_token值都是不一樣的,利用瀏覽器的無法跨站請求cookie的協(xié)議镜撩,webb無法請求weba的cookie

修改weba內(nèi)的transfer視圖预柒,添加驗證csrf_token的內(nèi)容:

import csrf_g

def transfer():
    # 取出cookie確保登錄
    username = request.cookies.get('username', '')
    if not username:
        # 無cookie說明未登錄,需要登錄
        return redirect(url_for('index'))
    if request.method == 'POST':
        account = request.form.get('account')
        money = request.form.get('money')

        # 取表單中的csrf_token
        form_csrf_token = request.form.get('csrf_token')
        # 取cookie內(nèi)csrf_token
        cookie_csrf_token = request.cookies.get('csrf_token')

        if form_csrf_token != cookie_csrf_token:
            return "非法操作"
        else:
            return '轉(zhuǎn)賬{}元到{}賬戶成功'.format(account, money)
    csrf_token = csrf_g.generate_csrf()
    # makeresponse 相當于Django內(nèi)的Httpresponse
    response = make_response(render_template('transfer.html', csrf_token=csrf_token))
    response.set_cookie('csrf_token', csrf_token)
    return response

而在多數(shù)實際操作的過程中袁梗,我們不必過于麻煩宜鸯,可以使用flask已封裝的方法,即CsrfProtect模塊遮怜,此處我們應用的是Flask 1.1.2淋袖。

我們找到之前我們用wtf表單進行的登錄驗證app,利用CsrfProtect模塊添加驗證內(nèi)容

from flask_wtf.csrf import CSRFProtect, CSRFError

CSRFProtect(app)

# 添加錯誤裝飾器
@app.errorhandler(CSRFError)
def csrf_error(reason):
    return render_template('csrf_error.html', reason=reason), 400

新建csrf_error.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
ERROR
</body>
</html>

修改wtf.html

<form action="" method="post">
    {{ form.username.label }}{{ form.username }}<br>
    {{ form.password.label }}{{ form.password }}<br>
    {{ form.password2.label }}{{ form.password2 }}<br>
    {{ form.submit }}<br>
    <input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
</form>

實現(xiàn)csrf保護

而對于添加csrf保護的方法不僅僅于此锯梁,具體內(nèi)容可通過官方文檔 CsrfProtect深入了解


III即碗、關聯(lián)數(shù)據(jù)庫方式SQLAlchemy

在學習完之前的預備內(nèi)容后,我們大致了解了Flask相關的一些核心內(nèi)容陌凳,而我們接下來要繼續(xù)深入了解Flask剥懒,而這就不得不了解Flask的ORM框架結(jié)構(gòu),因此冯遂,我們需要先體會一下Flask內(nèi)管理數(shù)據(jù)庫的方式蕊肥。在此處我們僅僅做一個引例谒获,在下一章中將繼續(xù)深入了解該內(nèi)容蛤肌。

在引例子中,我們將涉及一些關聯(lián)數(shù)據(jù)庫的基礎操作批狱,請自行體會裸准。

預備過程需要安裝 flask_sqlalchemy、mysqlclient并創(chuàng)建一個pure python 的項目test4赔硫,創(chuàng)建數(shù)據(jù)庫flask_test

編寫一個db_demo.py

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
# 設置數(shù)據(jù)庫連接
# 設置格式app.config['SQLALCHEMY_DATABASE_URI']  = 'mysql://用戶名:密碼@數(shù)據(jù)庫端口地址/數(shù)據(jù)庫名稱'
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:123456@127.0.0.1:3306/test_flask'
# 動態(tài)追蹤設置炒俱,可用于關閉某些Warning
app.config['SQLALCHEMY_TRACK_MODUFICATIONS'] = True
# 顯示原始sql
app.config['SQLALCHEMY_ECHO'] = True
# 設置每次請求后自動提交數(shù)據(jù)庫的改動
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True

# 數(shù)據(jù)庫要和 app關聯(lián),db為SQLAlchemy的一個實例
db = SQLAlchemy(app)

# 創(chuàng)建一個角色類作為例子爪膊,繼承于db
class Role(db.Model):
    # 定義表名
    __tablename__ = 'roles'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)


@app.route('/')
def index():
    return 'index'


if __name__ == '__main__':
    # 刪除表
    db.drop_all()
    # 創(chuàng)建表
    db.create_all()
    ro1 = Role(name='admin')
    ro2 = Role(name='user')
    db.session.add_all([ro1, ro2])
    db.session.commit()
    app.run(port=5005)

注:Flask在創(chuàng)建表時并不會自動創(chuàng)建id主鍵值和表名权悟,需要自行設計,這與Django是有區(qū)別的
運行服務后推盛,打開數(shù)據(jù)庫峦阁,觀察結(jié)果。


最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末耘成,一起剝皮案震驚了整個濱河市榔昔,隨后出現(xiàn)的幾起案子驹闰,更是在濱河造成了極大的恐慌,老刑警劉巖撒会,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嘹朗,死亡現(xiàn)場離奇詭異,居然都是意外死亡诵肛,警方通過查閱死者的電腦和手機屹培,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來怔檩,“玉大人惫谤,你說我怎么就攤上這事≈橄矗” “怎么了溜歪?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長许蓖。 經(jīng)常有香客問我蝴猪,道長,這世上最難降的妖魔是什么膊爪? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任自阱,我火速辦了婚禮,結(jié)果婚禮上米酬,老公的妹妹穿的比我還像新娘沛豌。我一直安慰自己,他們只是感情好赃额,可當我...
    茶點故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布加派。 她就那樣靜靜地躺著,像睡著了一般跳芳。 火紅的嫁衣襯著肌膚如雪芍锦。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天飞盆,我揣著相機與錄音娄琉,去河邊找鬼。 笑死吓歇,一個胖子當著我的面吹牛孽水,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播城看,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼女气,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了析命?” 一聲冷哼從身側(cè)響起主卫,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤逃默,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后簇搅,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體完域,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年瘩将,在試婚紗的時候發(fā)現(xiàn)自己被綠了吟税。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡姿现,死狀恐怖肠仪,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情备典,我是刑警寧澤异旧,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站提佣,受9級特大地震影響吮蛹,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜拌屏,卻給世界環(huán)境...
    茶點故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一潮针、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧倚喂,春花似錦每篷、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至枫笛,卻和暖如春吨灭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背刑巧。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留无畔,地道東北人啊楚。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像浑彰,于是被迫代替她去往敵國和親恭理。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,446評論 2 348