亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Python個(gè)人博客程序開(kāi)發(fā)實(shí)例用戶(hù)驗(yàn)證功能

 更新時(shí)間:2022年12月09日 10:31:31   作者:皮皮要HAPPY  
這篇文章主要介紹了怎樣用Python來(lái)實(shí)現(xiàn)一個(gè)完整的個(gè)人博客系統(tǒng),我們通過(guò)實(shí)操上手的方式可以高效的鞏固所學(xué)的基礎(chǔ)知識(shí),感興趣的朋友一起來(lái)看看吧

Python個(gè)人博客程序開(kāi)發(fā)實(shí)例框架設(shè)計(jì)中,我們已經(jīng)完成了 數(shù)據(jù)庫(kù)設(shè)計(jì)、數(shù)據(jù)準(zhǔn)備、模板架構(gòu)、表單設(shè)計(jì)、視圖函數(shù)設(shè)計(jì)、電子郵件支持 等總體設(shè)計(jì)的內(nèi)容。

Python個(gè)人博客程序開(kāi)發(fā)實(shí)例信息顯示中,我們一起實(shí)現(xiàn)了 顯示文章列表、博客信息、文章內(nèi)容和評(píng)論 等功能。

那么,本篇文章將會(huì)介紹如何 初始化博客、利用 Flask-Login 管理用戶(hù)認(rèn)證、使用 CSRFProtect 實(shí)現(xiàn) CSRF 保護(hù)。

1.安全存儲(chǔ)密碼

創(chuàng)建管理員用戶(hù)需要存儲(chǔ)用戶(hù)名和密碼,密碼的存儲(chǔ)需要特別注意。密碼不能直接以明文的形式存儲(chǔ)在數(shù)據(jù)庫(kù)中,因?yàn)橐坏?shù)據(jù)庫(kù)被竊取或是被攻擊者使用暴 力破解或字典法破解,用戶(hù)的賬戶(hù)、密碼將被直接泄露。如果發(fā)生泄漏,常常會(huì)導(dǎo)致用戶(hù)在其他網(wǎng)站上的賬戶(hù)處于危險(xiǎn)狀態(tài),因?yàn)橥ǔS脩?hù)會(huì)在多個(gè)網(wǎng)站使用同一個(gè)密碼。一般的做法是不存儲(chǔ)密碼本身,而是存儲(chǔ)通過(guò)密碼生成的散列值(hash)。每一個(gè)密碼對(duì)應(yīng)著獨(dú)一無(wú)二的散列值,從而避免明文存儲(chǔ)密碼。

如果只是簡(jiǎn)單地計(jì)算散列值,攻擊者可以使用彩虹表的方式逆向破解密碼。這時(shí)我們需要加鹽計(jì)算散列值。加鹽后,散列值的隨機(jī)性會(huì)顯著提高。但僅僅把鹽和散列值連接在一起可能還不夠,我們還需要使用 HMAC(hash-based message authentication code) 來(lái)重復(fù)計(jì)算很多次(比如 5000 次)最終獲得派生密鑰,這會(huì)增大攻擊者暴 力破解密碼的難度,這種方式被稱(chēng)為 密鑰擴(kuò)展(key stretching)。

在密碼學(xué)中,鹽(salt)是一串隨機(jī)生成的字符,用來(lái)增加散列值計(jì)算的隨機(jī)性。經(jīng)過(guò)這一系列處理后,即使攻擊者獲取到了密碼的散列值,也無(wú)法逆向獲取真實(shí)的密碼值。在生產(chǎn)環(huán)節(jié)中,盡管對(duì)密碼加密存儲(chǔ)安全性很強(qiáng),仍然需要使用安全的 HTTP 以加密傳輸數(shù)據(jù),避免密碼在傳輸過(guò)程中被截獲。

Werkzeug 在 security 模塊中提供了一個(gè) generate_password_hash(password,method=‘pbkdf2:sha256’,salt_length=8) 函數(shù)用于為給定的密碼生成散列值,參數(shù) method 用來(lái)指定計(jì)算散列值的方法,salt_length 參數(shù)用來(lái)指定鹽(salt)的長(zhǎng)度。security 模塊中的 check_password_hash(pwhash,password) 函數(shù)接收散列值(pwhash)和密碼(password)作為參數(shù),用于檢查密碼散列值與密碼是否對(duì)應(yīng)。

>>> from werkzeug.security import generate_password_hash, check_password_hash
>>> password_hash = generate_password_hash('cat')
>>> password_hash
'pbkdf2:sha256:50000$mIeMzTvb$ba3c0a274c6b53fda2ab39f864254dfb0a929848b7ec99f81e3bf721d8860fdc'
>>> check_password_hash(password_hash, 'dog')
False
>>> check_password_hash(password_hash, 'cat')
True
>>> password_hash = generate_password_hash('cat')
>>> password_hash
'pbkdf2:sha256:150000$AITKk6jv$5c0b732535cae83677fdf2e666153f82b5db30e6f40ec7a625678ad2b5f4ad25'

generate_password_hash() 函數(shù)生成的密碼散列值的格式如下:

method$salt$hash

因?yàn)樵谟?jì)算散列值時(shí)會(huì)加鹽,而鹽是隨機(jī)生成的,所以即使兩個(gè)用戶(hù)的密碼相同,最終獲得的密碼散列值也是不同的。我們沒(méi)法從密碼散列值逆向獲取密碼,但是如果密碼、計(jì)算方法、鹽相同,最終獲得的散列值結(jié)果也會(huì)是相同的,所以 check_password_hash() 函數(shù)會(huì)根據(jù)密碼散列值中的方法、鹽重新對(duì)傳入的密碼進(jìn)行散列值計(jì)算,然后對(duì)比散列值。

from werkzeug.security import generate_password_hash, check_password_hash
class User(db.Model):
	...
	password_hash = db.Column(db.String(128))
	...
	def set_password(self, password):
		self.password_hash = generate_password_hash(password)
	def validate_password(self, password):
		return check_password_hash(self.password_hash, password)

set_password() 方法用來(lái)設(shè)置密碼,它接收密碼的原始值作為參數(shù),將密碼的散列值設(shè)為 password_hash 的值。validate_password() 方法用于驗(yàn)證密碼是否和對(duì)應(yīng)的散列值相符,返回布爾值。

2.使用Flask-Login管理用戶(hù)認(rèn)證

博客程序需要根據(jù)用戶(hù)的身份開(kāi)放不同的功能,對(duì)于程序使用者——管理員來(lái)說(shuō),他可以撰寫(xiě)文章、管理博客;而普通的用戶(hù)(匿名用戶(hù))則只能閱讀文章、發(fā)表評(píng)論。為了讓程序識(shí)別出用戶(hù)的身份,我們需要添加用戶(hù)認(rèn)證功能。具體來(lái)說(shuō),使用用戶(hù)名和密碼登入博客程序的用戶(hù)被視為管理員,而未登錄的用戶(hù)則被視為匿名用戶(hù)。

擴(kuò)展 Flask-Login 為 Flask 提供了用戶(hù)會(huì)話(huà)管理功能,使用它可以輕松的處理用戶(hù)登錄、登出等操作。

extensions.py 腳本中實(shí)例化擴(kuò)展提供的 LoginManager 類(lèi),創(chuàng)建一個(gè) login_managerlogin 對(duì)象。

from flask_login import LoginManager
...
login_manager = LoginManager(app)

然后在程序包的工廠函數(shù)中對(duì) login 對(duì)象調(diào)用 init_app() 方法進(jìn)行初始化擴(kuò)展

login_manager.init_app(app)

Flask-Login 要求表示用戶(hù)的類(lèi)必須實(shí)現(xiàn)下表中所示的這幾個(gè)屬性和方法,以便用來(lái)判斷用戶(hù)的認(rèn)證狀態(tài)。

通過(guò)對(duì)用戶(hù)對(duì)象調(diào)用各種方法和屬性即可判斷用戶(hù)的狀態(tài),比如是否登錄等。方便的做法是讓用戶(hù)類(lèi)繼承 Flask-Login 提供的 UserMixin 類(lèi),它包含了這些方法和屬性的默認(rèn)實(shí)現(xiàn)。

from flask_login import UserMixin
class Admin(db.Model, UserMixin):
	...

UserMinxin 表示通過(guò)認(rèn)證的用戶(hù),所以 is_authenticatedis_active 屬性會(huì)返回 True,而 is_anonymous 則返回 False。get_id() 默認(rèn)會(huì)查找用戶(hù)對(duì)象的 id 屬性值作為 id,而這正是我們的 Admin 類(lèi)中的主鍵字段。

使用 Flask-Login 登入/登出某個(gè)用戶(hù)非常簡(jiǎn)單,只需要在視圖函數(shù)中調(diào)用 Flask-Login 提供的 login_user()logout_user() 函數(shù),并傳入要登入/登出的用戶(hù)類(lèi)對(duì)象。在這兩個(gè)函數(shù)背后,F(xiàn)lask-Login 使用 Flask 的 session 對(duì)象將用戶(hù)的 id 值存儲(chǔ)到用戶(hù)瀏覽器的 cookie 中(名為 user_id),這時(shí)表示用戶(hù)被登入。相對(duì)來(lái)說(shuō),登出則意味著在用戶(hù)瀏覽器的 cookie 中刪除這個(gè)值。默認(rèn)情況下,關(guān)閉瀏覽器時(shí),通過(guò) Flask 的 session 對(duì)象存儲(chǔ)在客戶(hù)端的 session cookie 會(huì)被刪除,所以用戶(hù)會(huì)登出。

另外,F(xiàn)lask-Login 還支持記住登錄狀態(tài),通過(guò)在 login_user() 中將 remember 參數(shù)設(shè)為 True 即可實(shí)現(xiàn)。這時(shí) Flask-Login 會(huì)在用戶(hù)瀏覽器中創(chuàng)建一個(gè)名為 remember_tokencookie,當(dāng)通過(guò) session 設(shè)置的 user_id cookie 因?yàn)橛脩?hù)關(guān)閉瀏覽器而失效時(shí),它會(huì)重新恢復(fù) user_id cookie 的值。

為了防止破壞 Flask-Login 提供的認(rèn)證功能,我們?cè)谝晥D函數(shù)中操作 session 時(shí)要避免使用 user_idremember_token 作為鍵。remember_token cookie 的默認(rèn)過(guò)期時(shí)間為 365 天。你可以通過(guò)配置變量 REMEMBER_COOKIE_DURATION 進(jìn)行設(shè)置,設(shè)為 datetime.timedelta 對(duì)象即可。

2.1 獲取當(dāng)前用戶(hù)

那么我們?nèi)绾闻袛嘤脩?hù)的認(rèn)證狀態(tài)呢?答案是使用 Flask-Login 提供的 current_user 對(duì)象。它是一個(gè)和 current_app 類(lèi)似的代理對(duì)象(Proxy),表示當(dāng)前用戶(hù)。調(diào)用時(shí)會(huì)返回與當(dāng)前用戶(hù)對(duì)應(yīng)的用戶(hù)模型類(lèi)對(duì)象。因?yàn)?session 中只會(huì)存儲(chǔ)登錄用戶(hù)的 id,所以為了讓它返回對(duì)應(yīng)的用戶(hù)對(duì)象,我們還需要設(shè)置一個(gè)用戶(hù)加載函數(shù)。這個(gè)函數(shù)需要使用 login_manager.user_loader 裝飾器,它接收用戶(hù) id 作為參數(shù),返回對(duì)應(yīng)的用戶(hù)對(duì)象。

@login_manager.user_loader
def load_user(user_id):
    from bluelog.models import Admin
    user = Admin.query.get(int(user_id))
    return user

現(xiàn)在,當(dāng)我們調(diào)用 current_user 時(shí),F(xiàn)lask-Login 會(huì)調(diào)用用戶(hù)加載函數(shù)并返回對(duì)應(yīng)的用戶(hù)對(duì)象。如果當(dāng)前用戶(hù)已經(jīng)登錄,會(huì)返回 Admin 類(lèi)實(shí)例;如果用戶(hù)未登錄,current_user 默認(rèn)會(huì)返回 Flask-Login 內(nèi)置的 AnonymousUserMixin 類(lèi)對(duì)象,它的 is_authenticatedis_active 屬性會(huì)返回 False,而 is_anonymous 屬性則返回 True。

current_user 存儲(chǔ)在請(qǐng)求上下文堆棧上,所以只有激活請(qǐng)求上下文程序的情況下才可以使用,比如在視圖函數(shù)中或是模板中調(diào)用。

最終,我們可以通過(guò)對(duì) current_user 對(duì)象調(diào)用 is_authenticated 等屬性來(lái)判斷當(dāng)前用戶(hù)的認(rèn)證狀態(tài)。它也和我們自定義的模板全局變量一樣注入到了模板上下文中,可以在所有模板中使用,所以我們可以在模板中根據(jù)用戶(hù)狀態(tài)渲染不同的內(nèi)容

2.2 登入用戶(hù)

個(gè)人博客的登錄鏈接可以放在次要的位置,因?yàn)橹挥胁┛妥髡卟艜?huì)真正用到它。我們把它放到頁(yè)腳,并根據(jù)用戶(hù)的狀態(tài)來(lái)選擇渲染出不同的鏈接。

<small>
	{% if current_user.is_authenticated %}
	<!-- 如果用戶(hù)已經(jīng)登錄,顯示下面的“登出”鏈接-->
	<a href="{{ url_for('auth.logout', next=request.full_path) }}" rel="external nofollow" >Logout</a>
	{% else %}
	<!-- 如果沒(méi)有登錄,則顯示下面的“登錄”按鈕 -->
	<a href="{{ url_for('auth.login', next=request.full_path) }}" rel="external nofollow" >Login</a>
	{% endif %}
</small>

通過(guò) current_useris_authenticated 值判斷用戶(hù)是否登錄,如果用戶(hù)已登錄(is_authenticatedTrue)就渲染注銷(xiāo)按鈕,否則就渲染登錄按鈕。按鈕中的 URL 分別指向用于登錄和登出的 loginlogout 視圖,url_for() 函數(shù)中加入的 next 參數(shù)用來(lái)存儲(chǔ)當(dāng)前頁(yè)面的路徑,以便在執(zhí)行登錄或登出操作后將用戶(hù)重定向回上一個(gè)頁(yè)面。

from flask_login import login_user
from bluelog.forms import LoginForm
from bluelog.models import Admin
from bluelog.utils import redirect_back
...
@auth_bp.route('/login', methods=['GET', 'POST'])
def login():
    if current_user.is_authenticated:
        return redirect(url_for('blog.index'))
    form = LoginForm()
    if form.validate_on_submit():
        username = form.username.data
        password = form.password.data
        remember = form.remember.data
        admin = Admin.query.first()
        if admin:
            # 驗(yàn)證用戶(hù)名和密碼
            if username == admin.username and admin.validate_password(password):
                login_user(admin, remember)  # 登入用戶(hù)
                flash('Welcome back.', 'info')
                return redirect_back()  # 返回上一個(gè)頁(yè)面
            flash('Invalid username or password.', 'warning')
        else:
            flash('No account.', 'warning')
    return render_template('auth/login.html', form=form)

登錄視圖負(fù)責(zé)渲染 login.html 模板和驗(yàn)證登錄表單。在函數(shù)一開(kāi)始,為了避免已經(jīng)登錄的用戶(hù)不小心訪(fǎng)問(wèn)這個(gè)視圖,我們添加一個(gè)if判斷將已經(jīng)登錄的用戶(hù)重定向到首頁(yè)。

與其他表單處理流程相同,當(dāng)用戶(hù)提交表單且數(shù)據(jù)通過(guò)驗(yàn)證后,我們分別從表單中獲取用戶(hù)名(username)、密碼(password)和 “記住我”(remember)字段的數(shù)據(jù)。接著,從數(shù)據(jù)庫(kù)中查詢(xún)出 Admin 對(duì)象,判斷 username 的值,并使用 Admin 類(lèi)中的 validate_password() 方法驗(yàn)證密碼。如果通過(guò)驗(yàn)證就調(diào)用 login_user() 方法登錄用戶(hù),傳入用戶(hù)對(duì)象和 remember 字段的值作為參數(shù),最后使用 redirect_back() 函數(shù)重定向回上一個(gè)頁(yè)面;如果用戶(hù)名和密碼驗(yàn)證出錯(cuò)就發(fā)送錯(cuò)誤提示,并渲染模板。另外,如果 Admin 對(duì)象不存在,就發(fā)送一個(gè)提示消息,然后重新渲染表單。

登錄表單 LoginForm 在新創(chuàng)建的 login.html 模板中使用 Bootstrap-Flask 提供的 render_form() 宏渲染。為了編寫(xiě)一個(gè)更簡(jiǎn)單的登錄頁(yè)面,我們打算不在登錄頁(yè)面顯示頁(yè)腳,因?yàn)槲覀冊(cè)诨0逯袨轫?yè)腳的代碼定義了 footer 塊,所以在登錄頁(yè)面模板只需要定義這個(gè)塊并留空就可以覆蓋基模板中的對(duì)應(yīng)內(nèi)容。

{% extends 'base.html' %}
{% from 'bootstrap/form.html' import render_form %}
{% block title %}Login{% endblock %}
{% block content %}
    <div class="container h-100">
        <div class="row h-100 page-header justify-content-center align-items-center">
            <h1>Log in</h1>
        </div>
        <div class="row h-100 justify-content-center align-items-center">
            {{ render_form(form, extra_classes='col-6') }}
        </div>
    </div>
{% endblock %}
{% block footer %}{% endblock %}

2.3 登出用戶(hù)

注銷(xiāo)登錄比登錄還要簡(jiǎn)單,只需要調(diào)用 Flask-Login 提供的 logout_user() 函數(shù)即可。這會(huì)登出用戶(hù)并清除 session 中存儲(chǔ)的用戶(hù) id 和 “記住我” 的值。

from flask_login import logout_user
...
@auth_bp.route('/logout')
@login_required
def logout():
	logout_user()
	flash('Logout success.', 'info')
	return redirect_back()

2.4 視圖保護(hù)

程序中的許多操作要求用戶(hù)登錄后才能進(jìn)行,因此我們要把這些需要登錄才能訪(fǎng)問(wèn)的視圖 “保護(hù)” 起來(lái)。如果用戶(hù)訪(fǎng)問(wèn)了某個(gè)需要認(rèn)證才能訪(fǎng)問(wèn)的資源,我們不會(huì)返回對(duì)應(yīng)的響應(yīng),而是把程序重定向到登錄頁(yè)面。

視圖保護(hù)可以使用 Flask-Login 提供的 login_required 裝飾器實(shí)現(xiàn)。在需要登錄才能訪(fǎng)問(wèn)的視圖前附加這個(gè)裝飾器,比如博客設(shè)置頁(yè)面。

當(dāng)為視圖函數(shù)附加多個(gè)裝飾器時(shí),route() 裝飾器應(yīng)該置于最外層。

from flask_login import login_required
@admin_bp.route('/settings')
@login_required
def settings():
	...
	return render_template('admin/settings.html')

當(dāng)未登錄的用戶(hù)訪(fǎng)問(wèn)使用了 login_required 裝飾器的視圖時(shí),程序會(huì)自動(dòng)重定向到登錄視圖,并閃現(xiàn)一個(gè)消息提示。在此之前,我們還需要在 extension.py 腳本中使用 login_manager 對(duì)象的 login_view 屬性設(shè)置登錄視圖的端點(diǎn)值(包含藍(lán)本名的完整形式)。

login_manager = LoginManager(app)
...
login_manager.login_view = 'auth.login'
login_manager.login_message_category = 'warning'

使用可選的 login_message_category 屬性可以設(shè)置消息的類(lèi)別,默認(rèn)類(lèi)別為 “message”。另外,使用可選的 login_message 屬性設(shè)置提示消息的內(nèi)容,默認(rèn)消息內(nèi)容為“Please log in to access this page.”

當(dāng)用戶(hù)訪(fǎng)問(wèn)某個(gè)被保護(hù)的 URL 時(shí),在重定向后的登錄 URL 中,F(xiàn)lask-Login 會(huì)自動(dòng)附加一個(gè)包含上一個(gè)頁(yè)面 URL 的 next 參數(shù),所以我們只需要使用 redirect_back() 函數(shù)就可以將登錄成功后的用戶(hù)重定向回上一個(gè)頁(yè)面。

當(dāng)在未登錄狀態(tài)下訪(fǎng)問(wèn)設(shè)置頁(yè)面 http://localhost:5000/admin/settings 時(shí),程序會(huì)重定向到登錄頁(yè)面,并顯示提示消息,URL 中包含上一個(gè)頁(yè)面的 next 參數(shù)。

仔細(xì)觀察地址欄,你會(huì)看到附加的 next 參數(shù)包含上一個(gè)頁(yè)面的地址,我們經(jīng)常在上網(wǎng)時(shí)在地址欄發(fā)現(xiàn)類(lèi)似的參數(shù),比如 ReturnUrlRedirectUrl 等。當(dāng)我們登錄后,程序會(huì)重定向回我們想要訪(fǎng)問(wèn)的設(shè)置頁(yè)面。

有些時(shí)候,你會(huì)希望為整個(gè)藍(lán)本添加登錄保護(hù)。比如,管理后臺(tái)的所有頁(yè)面都需要登錄后才能訪(fǎng)問(wèn),也就是說(shuō),我們需要為所有 admin 藍(lán)本中的視圖函數(shù)附加 login_required 裝飾器。有一個(gè)小技巧可以避免這些重復(fù):為 admin 藍(lán)本注冊(cè)一個(gè) before_request 處理函數(shù),然后為這個(gè)函數(shù)附加 login_required 裝飾器。因?yàn)槭褂?before_request 鉤子注冊(cè)的函數(shù)會(huì)在每一個(gè)請(qǐng)求前運(yùn)行,所以這樣就可以為該藍(lán)本下所有的視圖函數(shù)添加保護(hù),函數(shù)內(nèi)容可以為空。

@admin_bp.before_request
@login_required
def login_protect():
	pass
  • 雖然這個(gè)技巧很方便,但是為了避免在書(shū)中單獨(dú)給出視圖函數(shù)代碼時(shí)造成誤解,Bluelog程序中并沒(méi)有使用這個(gè)技巧。
  • 如果沒(méi)有使用這個(gè)技巧,那么 admin 藍(lán)本下的所有視圖都需要添加 login_required 裝飾器,否則會(huì)導(dǎo)致博客資源被匿名用戶(hù)修改。

3.使用CSRFProtect實(shí)現(xiàn)CSRF保護(hù)

CSRF攻擊,全稱(chēng)為 Cross-site request forgery,中文名為 跨站請(qǐng)求偽造,也被稱(chēng)為 One Click Attack 或者 Session Riding,通??s寫(xiě)為 CSRF 或者 XSRF,是一種對(duì)網(wǎng)站的惡意利用。XSS 主要是利用站點(diǎn)內(nèi)的信任用戶(hù),而 CSRF 則通過(guò)偽裝來(lái)自受信任用戶(hù)的請(qǐng)求,來(lái)利用受信任的網(wǎng)站。與 XSS 相比,CSRF 更具危險(xiǎn)性。攻擊者盜用用戶(hù)身份,發(fā)送惡意請(qǐng)求。比如:模擬用戶(hù)發(fā)送郵件,發(fā)消息,以及支付、轉(zhuǎn)賬等。

博客管理后臺(tái)會(huì)涉及對(duì)資源的局部更新和刪除操作,這時(shí)我們就要考慮到 CSRF 保護(hù)問(wèn)題。為了應(yīng)對(duì) CSRF 攻擊,當(dāng)需要?jiǎng)?chuàng)建、修改和刪除數(shù)據(jù)時(shí),我們需要將這類(lèi)請(qǐng)求通過(guò) POST 方法提交,同時(shí)在提交請(qǐng)求的表單中添加 CSRF 令牌。對(duì)于刪除和某些修改操作來(lái)說(shuō),單獨(dú)創(chuàng)建表單類(lèi)的流程太過(guò)煩瑣,我們可以使用 Flask-WTF 內(nèi)置的 CSRFProtect 擴(kuò)展為這類(lèi)操作實(shí)現(xiàn)更簡(jiǎn)單和完善的 CSRF 保護(hù)。

CSRFProtect 是 Flask-WTF 內(nèi)置的擴(kuò)展,也是 Flask-WTF 內(nèi)部使用的 CSRF 組件,單獨(dú)使用可以實(shí)現(xiàn)對(duì)程序的全局 CSRF 保護(hù)。它主要提供了生成和驗(yàn)證 CSRF 令牌的函數(shù),方便在不使用 WTForms 表單類(lèi)的情況下實(shí)現(xiàn) CSRF 保護(hù)。因?yàn)槲覀円呀?jīng)安裝了 Flask-WTF,所以可以直接使用它。首先在 extensions.py 腳本中實(shí)例化 Flask-WTF 提供的 CSRFProtect 類(lèi)。

from flask_wtf.csrf import CSRFProtect
...
csrf = CSRFProtect()
...

在程序包的構(gòu)造文件中初始化擴(kuò)展 CSRFProtect

from bluelog.extensions import csrf
def create_app(config_name=None):
	...
	register_extensions(app)
	return app
def register_extensions(app):
	...
	csrf.init_app(app)

CSRFProtect 在模板中提供了一個(gè) csrf_token() 函數(shù),用來(lái)生成 CSRF 令牌值,我們直接在表單中創(chuàng)建這個(gè)隱藏字段,將這個(gè)字段的 name 值設(shè)為 csrf_token。下面是用來(lái)刪除文章的表單示例:

<form method="post" action="{{ url_for('.delete_post', post_id=post.id) }}">
	<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
	<input type="submit" value="Delete Post"/>
</form>

在對(duì)應(yīng)的 delete_post 視圖中,我們直接執(zhí)行相關(guān)刪除操作,CSRFProtect 會(huì)自動(dòng)獲取并驗(yàn)證 CSRF 令牌。注意,在 app.route() 裝飾器中使用 methods 參數(shù)限制僅監(jiān)聽(tīng) POST 請(qǐng)求。

@app.route('/post/delete/<id>', methods=['POST'])
def delete_post(id):
	post = Post.query.get(id)
	post.delete()
	return redirect(url_for('index'))

默認(rèn)情況下,當(dāng)令牌驗(yàn)證出錯(cuò)或過(guò)期時(shí),程序會(huì)返回 400 錯(cuò)誤,和 Werkzeug 內(nèi)置的其他 HTTP 異常類(lèi)一樣,CSRFError 將錯(cuò)誤描述保存在異常對(duì)象的 description 屬性中。

如果你想將與 CSRF 相關(guān)的錯(cuò)誤描述顯示在模板中,那么你可以在 400 錯(cuò)誤處理函數(shù)中將異常對(duì)象的 description 屬性傳入模板,也可以單獨(dú)創(chuàng)建一個(gè)錯(cuò)誤處理函數(shù)捕捉令牌出錯(cuò)時(shí)拋出的 CSRFError 異常。

from flask_wtf.csrf import CSRFError
def register_errors(app):
	...
	@app.errorhandler(CSRFError)
	def handle_csrf_error(e):
		return render_template('400.html', description=e.description), 400

這個(gè)錯(cuò)誤處理函數(shù)仍然使用 app.errorhandler 裝飾器注冊(cè),傳入 flask_wtf.csrf 模塊中的 CSRFError 類(lèi)。這個(gè)錯(cuò)誤處理函數(shù)返回 400 錯(cuò)誤響應(yīng),通過(guò)異常對(duì)象的 description 屬性獲取內(nèi)置的錯(cuò)誤消息(英文),傳入模板 400.html 中。在模板中,我們渲染這個(gè)錯(cuò)誤消息,并為常規(guī) 400 錯(cuò)誤設(shè)置一個(gè)默認(rèn)值。

<p>{<!--{cke_protected}{C}%3C!%2D%2D%20%2D%2D%3E-->{ description|default('Bad Request') }}</p>

在實(shí)際應(yīng)用中,除了使用內(nèi)置的錯(cuò)誤描述,更合適的方法是自己編寫(xiě)錯(cuò)誤描述信息。默認(rèn)的錯(cuò)誤描述為 “Invalid CSRF token.” 和 “The CSRF token is missing.” 因?yàn)榘嘈g(shù)語(yǔ),不容易理解,所以在實(shí)際的程序中,我們應(yīng)該使用更簡(jiǎn)單的錯(cuò)誤提示,比如 “會(huì)話(huà)過(guò)期或失效,請(qǐng)返回上一頁(yè)面重試”。

到此這篇關(guān)于Python個(gè)人博客程序開(kāi)發(fā)實(shí)例用戶(hù)驗(yàn)證功能的文章就介紹到這了,更多相關(guān)Python個(gè)人博客內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Python中True(真)和False(假)判斷詳解

    Python中True(真)和False(假)判斷詳解

    眾所周知True和False是一個(gè)布爾變量可取的值,下面這篇文章主要給大家介紹了關(guān)于Python中True(真)和False(假)判斷的相關(guān)資料,本文通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-07-07
  • Python數(shù)據(jù)類(lèi)型詳解(二)列表

    Python數(shù)據(jù)類(lèi)型詳解(二)列表

    本文給大家詳細(xì)介紹的是Python數(shù)據(jù)類(lèi)型中的列表(list),非常的簡(jiǎn)單實(shí)用,有需要的小伙伴可以參考下
    2016-05-05
  • Python格式化文本段落之textwrap庫(kù)

    Python格式化文本段落之textwrap庫(kù)

    這篇文章主要介紹了Python格式化文本段落之textwrap庫(kù),文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)python的小伙伴們有很大的幫助喲,需要的朋友可以參考下
    2021-05-05
  • Python 如何讀取字典的所有鍵-值對(duì)

    Python 如何讀取字典的所有鍵-值對(duì)

    這篇文章主要介紹了Python 讀取字典的所有鍵-值對(duì)操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-05-05
  • Python學(xué)習(xí)之基礎(chǔ)語(yǔ)法介紹

    Python學(xué)習(xí)之基礎(chǔ)語(yǔ)法介紹

    大家好,本篇文章主要講的是Python學(xué)習(xí)之基礎(chǔ)語(yǔ)法介紹,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話(huà)記得收藏一下,方便下次瀏覽
    2021-12-12
  • python3基礎(chǔ)之集合set詳解

    python3基礎(chǔ)之集合set詳解

    大家好,本篇文章主要講的是python3基礎(chǔ)之集合set詳解,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話(huà)記得收藏一下,方便下次瀏覽
    2021-12-12
  • Python的命令行參數(shù)實(shí)例詳解

    Python的命令行參數(shù)實(shí)例詳解

    python中有一個(gè)模塊sys,sys.argv這個(gè)屬性提供了對(duì)命令行參數(shù)的訪(fǎng)問(wèn),下面這篇文章主要給大家介紹了關(guān)于Python命令行參數(shù)實(shí)例的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-02-02
  • Python循環(huán)語(yǔ)句介紹

    Python循環(huán)語(yǔ)句介紹

    大家好,本篇文章主要講的是Python循環(huán)語(yǔ)句介紹,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話(huà)記得收藏一下,方便下次瀏覽
    2021-12-12
  • 一文詳細(xì)介紹PyQt5 QPushButton() 的作用

    一文詳細(xì)介紹PyQt5 QPushButton() 的作用

    通過(guò)本文的介紹,相信你已經(jīng)對(duì)PyQt5中的QPushButton控件有了深入的了解,從基礎(chǔ)介紹到常用屬性和方法,再到應(yīng)用場(chǎng)景和樣式定制,本文為你提供了全面的指南,感興趣的朋友跟隨小編一起看看吧
    2024-08-08
  • python比較兩個(gè)列表是否相等的方法

    python比較兩個(gè)列表是否相等的方法

    這篇文章主要介紹了python比較兩個(gè)列表是否相等的方法,實(shí)例分析了Python中==和is兩種方法的區(qū)別,需要的朋友可以參考下
    2015-07-07

最新評(píng)論