最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

使用Python的Flask框架表單插件Flask-WTF實(shí)現(xiàn)Web登錄驗(yàn)證

 更新時(shí)間:2016年07月12日 17:25:10   作者:邪惡的Sheldon  
Flask處理表單除了本身的WTForms包,使用Flask-WTF擴(kuò)展來增強(qiáng)表單功能也是很多開發(fā)者的選擇,這里我們就來講解如何使用Python的Flask框架表單插件Flask-WTF實(shí)現(xiàn)Web登錄驗(yàn)證

表單是讓用戶與我們的網(wǎng)頁應(yīng)用程序交互的基本元素。Flask 本身并不會(huì)幫助我們處理表單,但是 Flask-WTF 擴(kuò)展讓我們在我們的 Flask 應(yīng)用程序中使用流行的 WTForms 包。這個(gè)包使得定義表單和處理提交容易一些。

Flask-WTF
我們想要使用 Flask-WTF 做的第一件事情(在安裝它以后,GitHub項(xiàng)目頁:https://github.com/lepture/flask-wtf )就是在 myapp.forms 包中定義一個(gè)表單。

# ourapp/forms.py

from flask_wtf import Form
from wtforms import StringField, PasswordField
from wtforms.validators import DataRequired, Email

class EmailPasswordForm(Form):
 email = StringField('Email', validators=[DataRequired(), Email()])
 password = PasswordField('Password', validators=[DataRequired()])

在 Flask-WTF 0.9 版本以前,F(xiàn)lask-WTF 提供了針對 WTForms 字段以及驗(yàn)證器的自己的封裝。你可能看到外面一大堆的代碼是從 flask.ext.wtforms 中不是從 wtforms 中導(dǎo)入 TextField,PasswordField。
在 Flask-WTF 0.9 版本以后,我們應(yīng)該直接從 wtforms 中導(dǎo)入這些字段和驗(yàn)證器。
我們定義的表單是一個(gè)用戶登錄表單。我們把它叫做 EmailPasswordForm(),我們可以重用這個(gè)同樣的表單類(Form)去做其它的一些事情,像注冊表單。這里我們沒有去定義一個(gè)又長又沒有用的表單,而是選擇一個(gè)很常用的表單,只是為了給你們介紹使用 Flask-WTF 定義表單的方式。也許以后在正式項(xiàng)目中會(huì)定義一個(gè)特別復(fù)雜表單。對于表單中包含字段名稱,我們建議使用一個(gè)清楚的名稱,并且在一個(gè)表單中保持唯一。不得不說,對于一個(gè)長的表單,我們可能要給出一個(gè)更符合上文的字段名稱。

登錄表單可以替我們做一些事情。它能夠保證我們應(yīng)用程序的安全以防止 CSRF 漏洞,驗(yàn)證用戶輸入并且渲染適當(dāng)?shù)臉?biāo)記,這些標(biāo)記是我們?yōu)楸韱味x的字段。

CSRF 保護(hù)和驗(yàn)證
CSRF 表示跨站請求偽造。CSRF 攻擊是指第三方偽造(像一個(gè)表單提交)請求到一個(gè)應(yīng)用程序的服務(wù)器。一個(gè)易受攻擊的服務(wù)器假設(shè)從一個(gè)表單來的數(shù)據(jù)是來自它自己的網(wǎng)站并且采取相應(yīng)的操作。

作為一個(gè)例子,比方說,一個(gè)郵件提供商可以讓你通過提交一個(gè)表單來刪除你的賬號(hào)。表單發(fā)送一個(gè) POST 請求到服務(wù)器上的 account_delete 端點(diǎn)并且當(dāng)表單被提交的時(shí)候刪除登錄的賬號(hào)。我們可以在自己的網(wǎng)站上創(chuàng)建一個(gè)表單,該表單發(fā)送一個(gè) POST 請求到同一個(gè) account_delete 端點(diǎn)?,F(xiàn)在,如果我們讓某人點(diǎn)擊我們表單的提交按鈕(或者通過 JavaScript 來這樣做),郵件提供商提供的登錄賬號(hào)就會(huì)被刪除掉。當(dāng)然郵件提供商還不知道表單提交并不是發(fā)生在他們的網(wǎng)站上。

因此如何才能阻止 POST 請求來自別的網(wǎng)站?WTForms 通過在渲染每一個(gè)表單的時(shí)候生成一個(gè)唯一的令牌使得成為可能。生成的令牌會(huì)被傳回到服務(wù)器,伴隨著 POST 請求的數(shù)據(jù),在表單被接受之前令牌必須接受服務(wù)器的驗(yàn)證。關(guān)鍵的是令牌是與存儲(chǔ)在用戶會(huì)話(cookies)的一個(gè)值有關(guān)并且會(huì)在一段時(shí)間后失效(默認(rèn)是 30 分鐘)。這種方式就能夠保證提交一個(gè)有效表單的人就是加載頁面的人(或者至少是使用同一電腦的人),而且他們只能在加載頁面 30 分鐘內(nèi)這樣做。

要開始使用 Flask-WTF 保護(hù) CSRF,我們需要為我們的登錄頁定義一個(gè)視圖。

# ourapp/views.py

from flask import render_template, redirect, url_for

from . import app
from .forms import EmailPasswordForm

@app.route('/login', methods=["GET", "POST"])
def login():
 form = EmailPasswordForm()
 if form.validate_on_submit():

  # Check the password and log the user in
  # [...]

  return redirect(url_for('index'))
 return render_template('login.html', form=form)

如果表單已經(jīng)被提交和驗(yàn)證的話,我們可以繼續(xù)登錄的邏輯。如果它沒有被提交的話(例如,只是一個(gè) GET 請求),我們就要把表單對象傳遞給我們的模板,以便它能夠被渲染。下面就是我們使用 CSRF 保護(hù)的時(shí)候模板的樣子。

{# ourapp/templates/login.html #}

{% extends "layout.html" %}
{% endraw %}
<html>
 <head>
  <title>Login Page</title>
 </head>
 <body>
  <form action="{{ url_for('login') }}" method="post">
   <input type="text" name="email" />
   <input type="password" name="password" />
   {{ form.csrf_token }}
  </form>
 </body>
</html>

{% raw %}{{ form.csrf_token }}{% endraw %} 渲染了一個(gè)隱藏的字段,該字段包含那些奇特的 CSRF 令牌,并且當(dāng) WTForms 驗(yàn)證表單的時(shí)候會(huì)尋找這個(gè)字段。我們不用擔(dān)心包含處理令牌的邏輯,WTForms 會(huì)主動(dòng)幫我們?nèi)プ?。好哇?/p>

自定義驗(yàn)證
除了由 WTForms 提供的內(nèi)置的表單驗(yàn)證器(例如,Required(),Email() 等等),我們能創(chuàng)建我們自己的驗(yàn)證器。我們將通過編寫一個(gè) Unique() 驗(yàn)證器來說明如何創(chuàng)建自己的驗(yàn)證器,Unique() 驗(yàn)證器是用來檢查數(shù)據(jù)庫并且確保用戶提供的值在數(shù)據(jù)庫中不存在。這能夠用于確保用戶名或者郵箱地址還沒有使用。沒有 WTForms 的話,我們可能要在視圖中做這些事情,但是現(xiàn)在我們可以在表單本身做些事情。

現(xiàn)在我們來定義一個(gè)簡單的注冊表單,其實(shí)這個(gè)表單和登錄的表單幾乎一樣。只是會(huì)在后面給它添加一些自定義的驗(yàn)證器。

# ourapp/forms.py

from flask_wtf import Form
from wtforms import StringField, PasswordField
from wtforms.validators import DataRequired, Email

class EmailPasswordForm(Form):
 email = StringField('Email', validators=[DataRequired(), Email()])
 password = PasswordField('Password', validators=[DataRequired()])

現(xiàn)在我們要添加我們的驗(yàn)證器用來確保它們提供的郵箱地址不存在數(shù)據(jù)庫中。我們把這個(gè)驗(yàn)證器放在一個(gè)新的 util 模塊,util.validators。

# ourapp/util/validators.py
from wtforms.validators import ValidationError

class Unique(object):
 def __init__(self, model, field, message=u'This element already exists.'):
  self.model = model
  self.field = field

 def __call__(self, form, field):
  check = self.model.query.filter(self.field == field.data).first()
  if check:
   raise ValidationError(self.message)

這個(gè)驗(yàn)證器假設(shè)我們是使用 SQLAlchemy 來定義我們的模型。WTForms 期待驗(yàn)證器返回某種可調(diào)用的對象(例如,一個(gè)可調(diào)用的類)。

在 Unique() 的 \_\_init\_\_ 中我們可以指定哪些參數(shù)傳入到驗(yàn)證器中,在本例中我們要傳入相關(guān)的模型(例如,在我們例子中是傳入 User 模型)以及要檢查的字段。當(dāng)驗(yàn)證器被調(diào)用的時(shí)候,如果定義模型的任何實(shí)例匹配表單中提交的值,它將會(huì)拋出一個(gè) ValidationError。我們也可以添加一個(gè)具有通用默認(rèn)值的消息,它將會(huì)被包含在 ValidationError 中。

現(xiàn)在我們可以修改 EmailPasswordForm,使用我們自定義的 Unique 驗(yàn)證器。

# ourapp/forms.py

from flask_wtf import Form
from wtforms import StringField, PasswordField
from wtforms.validators import DataRequired

from .util.validators import Unique
from .models import User

class EmailPasswordForm(Form):
 email = StringField('Email', validators=[DataRequired(), Email(),
  Unique(
   User,
   User.email,
   message='There is already an account with that email.')])
 password = PasswordField('Password', validators=[DataRequired()])

渲染表單
WTForms 也能幫助我們?yōu)楸韱武秩境?HTML 表示。WTForms 實(shí)現(xiàn)的 Field 字段能夠渲染成該字段的 HTML 表示,所以為了渲染它們,我們只必須在我們模板中調(diào)用表單的字段。這就像渲染 csrf_token 字段。下面給出了一個(gè)登錄模板的示例,在里面我們使用 WTForms 來渲染我們的字段。

{# ourapp/templates/login.html #}

{% extends "layout.html" %}
<html>
 <head>
  <title>Login Page</title>
 </head>
 <body>
  <form action="" method="post">
   {{ form.email }}
   {{ form.password }}
   {{ form.csrf_token }}
  </form>
 </body>
</html>

我們可以自定義如何渲染字段,通過傳入字段的屬性作為參數(shù)到調(diào)用中。

<form action="" method="post">
 {{ form.email.label }}: {{ form.email(placeholder='yourname@email.com') }}
 <br>
 {% raw %}{{ form.password.label }}: {{ form.password }}{% endraw %}
 <br>
 {% raw %}{{ form.csrf_token }}{% endraw %}
</form>

處理 OpenID 登錄
現(xiàn)實(shí)生活中,我們發(fā)現(xiàn)有很多人都不知道他們擁有一些公共賬號(hào)。一部分大牌的網(wǎng)站或服務(wù)商都會(huì)為他們的會(huì)員提供公共賬號(hào)的認(rèn)證。舉個(gè)栗子,如果你有一個(gè) google 賬號(hào),其實(shí)你就有了一個(gè)公共賬號(hào),類似的還有 Yahoo, AOL, Flickr 等。
為了方便我們的用戶能簡單的使用他們的公共賬號(hào),我們將把這些公共賬號(hào)的鏈接添加到一個(gè)列表,這樣用戶就不用自手工輸入了。

我們要把一些提供給用戶的公共賬號(hào)服務(wù)商定義到一個(gè)列表里面,這個(gè)列表就放到配置文件中吧 (fileconfig.py):

CSRF_ENABLED = True
SECRET_KEY = 'you-will-never-guess'
 
OPENID_PROVIDERS = [
 { 'name': 'Google', 'url': 'https://www.google.com/accounts/o8/id' },
 { 'name': 'Yahoo', 'url': 'https://me.yahoo.com' },
 { 'name': 'AOL', 'url': 'http://openid.aol.com/<username>' },
 { 'name': 'Flickr', 'url': 'http://www.flickr.com/<username>' },
 { 'name': 'MyOpenID', 'url': 'https://www.myopenid.com' }]

接下來就是要在我們的登錄視圖函數(shù)中使用這個(gè)列表了:

@app.route('/login', methods = ['GET', 'POST'])
def login():
 form = LoginForm()
 if form.validate_on_submit():
  flash('Login requested for OpenID="' + form.openid.data + '", remember_me=' + str(form.remember_me.data))
  return redirect('/index')
 return render_template('login.html',
  title = 'Sign In',
  form = form,
  providers = app.config['OPENID_PROVIDERS'])

我們從 app.config 中引入了公共賬號(hào)服務(wù)商的配置列表,然后把它作為一個(gè)參數(shù)通過 render_template 函數(shù)引入到模板。

接下來要做的我想你也猜得到,我們需要在登錄模板中把這些服務(wù)商鏈接顯示出來。

<!-- extend base layout -->
{% extends "base.html" %}
 
{% block content %}
<script type="text/javascript">
function set_openid(openid, pr)
{
 u = openid.search('<username>')
 if (u != -1) {
  // openid requires username
  user = prompt('Enter your ' + pr + ' username:')
  openid = openid.substr(0, u) + user
 }
 form = document.forms['login'];
 form.elements['openid'].value = openid
}
</script>
<h1>Sign In</h1>
<form action="" method="post" name="login">
 {{form.hidden_tag()}}
 <p>
  Please enter your OpenID, or select one of the providers below:<br>
  {{form.openid(size=80)}}
  {% for error in form.errors.openid %}
  <span style="color: red;">[{{error}}]</span>
  {% endfor %}<br>
  |{% for pr in providers %}
  <a href="javascript:set_openid('{{pr.url}}', '{{pr.name}}');">{{pr.name}}</a> |
  {% endfor %}
 </p>
 <p>{{form.remember_me}} Remember Me</p>
 <p><input type="submit" value="Sign In"></p>
</form>
{% endblock %}

這次的模板添加的東西似乎有點(diǎn)多。一些公共賬號(hào)需要提供用戶名,為了解決這個(gè)我們用了點(diǎn) javascript。當(dāng)用戶點(diǎn)擊相關(guān)的公共賬號(hào)鏈接時(shí),需要用戶名的公共賬號(hào)會(huì)提示用戶輸入用戶名, javascript 會(huì)把用戶名處理成可用的公共賬號(hào),最后再插入到 openid 字段的文本框中。

下面這個(gè)是在登錄頁面點(diǎn)擊 google 鏈接后顯示的截圖:

2016712172533772.jpg (450×300)

相關(guān)文章

  • 淺談Python traceback的優(yōu)雅處理

    淺談Python traceback的優(yōu)雅處理

    這篇文章主要介紹了淺談Python traceback的優(yōu)雅處理,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-08-08
  • Python pytest裝飾器總結(jié)(實(shí)例詳解)

    Python pytest裝飾器總結(jié)(實(shí)例詳解)

    這篇文章主要介紹了Python pytest裝飾器總結(jié),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-04-04
  • python讀寫Excel表格的實(shí)例代碼(簡單實(shí)用)

    python讀寫Excel表格的實(shí)例代碼(簡單實(shí)用)

    這篇文章主要介紹了python讀寫Excel表格的方法,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-12-12
  • 利用Python編寫的實(shí)用運(yùn)維腳本分享

    利用Python編寫的實(shí)用運(yùn)維腳本分享

    Python在很大程度上可以對shell腳本進(jìn)行替代。筆者一般單行命令用shell,復(fù)雜點(diǎn)的多行操作就直接用Python了。本文歸納了Python中一些實(shí)用腳本操作,需要的可以參考一下
    2022-05-05
  • python opencv將表格圖片按照表格框線分割和識(shí)別

    python opencv將表格圖片按照表格框線分割和識(shí)別

    這篇文章主要介紹了python opencv將表格圖片按照表格框線分割和識(shí)別,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-10-10
  • 分析Python字符串拼接+=和join()哪個(gè)速度更快

    分析Python字符串拼接+=和join()哪個(gè)速度更快

    這篇文章主要分析了Python中字符串拼接+=和join()哪個(gè)速度更快,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-02-02
  • PyTorch一小時(shí)掌握之遷移學(xué)習(xí)篇

    PyTorch一小時(shí)掌握之遷移學(xué)習(xí)篇

    這篇文章主要介紹了PyTorch一小時(shí)掌握之遷移學(xué)習(xí)篇,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-09-09
  • django框架實(shí)現(xiàn)一次性上傳多個(gè)文件功能示例【批量上傳】

    django框架實(shí)現(xiàn)一次性上傳多個(gè)文件功能示例【批量上傳】

    這篇文章主要介紹了django框架實(shí)現(xiàn)一次性上傳多個(gè)文件功能,結(jié)合實(shí)例形式分析了Django框架批量上傳相關(guān)實(shí)現(xiàn)技巧與操作注意事項(xiàng),需要的朋友可以參考下
    2019-06-06
  • Python編程之黑板上排列組合,你舍得解開嗎

    Python編程之黑板上排列組合,你舍得解開嗎

    這篇文章主要介紹了Python排列組合算法的相關(guān)內(nèi)容,具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-10-10
  • Python常用斷言函數(shù)實(shí)例匯總

    Python常用斷言函數(shù)實(shí)例匯總

    這篇文章主要介紹了Python常用斷言函數(shù)實(shí)例匯總,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-11-11

最新評論

宁国市| 措美县| 墨脱县| 招远市| 通化县| 斗六市| 南郑县| 昌图县| 孟村| 凤庆县| 如东县| 电白县| 应用必备| 华宁县| 民和| 新化县| 诸暨市| 宜州市| 正镶白旗| 门头沟区| 汪清县| 淮北市| 洪湖市| 西青区| 江华| 北辰区| 柯坪县| 徐水县| 民勤县| 温州市| 科尔| 福泉市| 安阳市| 翁源县| 孙吴县| 鸡东县| 瑞安市| 张掖市| 金湖县| 大姚县| 从化市|