首頁 後端開發 Python教學 使用Python的Flask框架表單插件Flask-WTF實現Web登入驗證

使用Python的Flask框架表單插件Flask-WTF實現Web登入驗證

Aug 04, 2016 am 08:55 AM
flask flask-wtf python 表單

表單是讓使用者與我們的網頁應用程式互動的基本元素。 Flask 本身並不會幫助我們處理表單,但是 Flask-WTF 擴充功能讓我們在我們的 Flask 應用程式中使用流行的 WTForms 套件。這個包使得定義表單和處理提交變得容易一些。

Flask-WTF
我們想要使用 Flask-WTF 做的第一件事情(在安裝它以後,GitHub專案頁:https://github.com/lepture/flask-wtf )就是在 myapp.forms 套件中定義一個表單。

# 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 版本以前,Flask-WTF 提供了針對 WTForms 欄位以及驗證器的自己的封裝。你可能會看到外面一大堆的程式碼是從 flask.ext.wtforms 不是從 wtforms 匯入 TextField,PasswordField。
在 Flask-WTF 0.9 版本以後,我們應該直接從 wtforms 中匯入這些欄位和驗證器。
我們定義的表單是使用者登入表單。我們把它叫做 EmailPasswordForm(),我們可以重複使用這個同樣的表單類別(Form)去做它的一些事情,像是註冊表單。這裡我們沒有去定義一個又長又沒用的表單,而是選擇一個很常用的表單,只是為了給你們介紹使用 Flask-WTF 定義表單的方式。也許以後在正式專案中會定義一個特別複雜表單。對於表單中包含欄位名稱,我們建議使用一個清楚的名稱,並且在一個表單中保持唯一。不得不說,對於一個長的表單,我們可能要給出一個更符合上文的欄位名稱。

登入表單可以替我們做些什麼。它能夠保證我們應用程式的安全性以防止 CSRF 漏洞,驗證使用者輸入並且渲染適當的標記,這些標記是我們為表單定義的欄位。

CSRF 保護與驗證
CSRF 表示跨站請求偽造。 CSRF 攻擊是指第三方偽造(像表單提交)請求到一個應用程式的伺服器。一個易受攻擊的伺服器假設從一個表單來的資料是來自它自己的網站並且採取相應的操作。

作為一個例子,比方說,一個郵件提供者可以讓你透過提交一個表單來刪除你的帳號。表單發送一個 POST 請求到伺服器上的 account_delete 端點並且當表單被提交的時候刪除登入的帳號。我們可以在自己的網站上建立一個表單,該表單會傳送一個 POST 請求到同一個 account_delete 端點。現在,如果我們讓某人點擊我們表單的提交按鈕(或透過 JavaScript 來這樣做),郵件提供者提供的登入帳號就會被刪除掉。當然郵件提供者還不知道表單提交並不是發生在他們的網站上。

因此如何才能阻止 POST 請求來自別的網站? WTForms 透過在渲染每個表單的時候產生一個唯一的令牌使得成為可能。產生的令牌會被傳回伺服器,伴隨著 POST 請求的數據,在表單被接受之前令牌必須接受伺服器的驗證。關鍵的是令牌是與儲存在使用者會話(cookies)的一個值有關並且會在一段時間後失效(預設是 30 分鐘)。這種方式就能夠保證提交一個有效表單的人就是載入頁面的人(或至少是使用同一台電腦的人),而且他們只能在載入頁面 30 分鐘內這樣做。

要開始使用 Flask-WTF 保護 CSRF,我們需要為我們的登入頁定義一個視圖。

# 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)

登入後複製

如果表單已經被提交和驗證的話,我們可以繼續登入的邏輯。如果它沒有被提交的話(例如,只是一個 GET 請求),我們就要把表單物件傳遞給我們的模板,以便它能夠被渲染。下面就是我們使用 CSRF 保護的時候模板的樣子。

{# 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 %} 渲染了一個隱藏的字段,該字段包含那些奇特的 CSRF 令牌,並且當 WTForms 驗證表單的時候會尋找這個字段。我們不用擔心包含處理令牌的邏輯,WTForms 會主動幫我們做。好哇!

自訂驗證
除了由 WTForms 提供的內建的表單驗證器(例如,Required(),Email() 等等),我們可以建立我們自己的驗證器。我們將透過編寫一個 Unique() 驗證器來說明如何建立自己的驗證器,Unique() 驗證器是用來檢查資料庫並且確保使用者提供的值在資料庫中不存在。這能夠用於確保使用者名稱或郵箱地址尚未使用。沒有 WTForms 的話,我們可能要在視圖中做這些事情,但是現在我們可以在表單本身做些事情。

現在我們來定義一個簡單的註冊表單,其實這個表單和登入的表單幾乎一樣。只是會在後面給它添加一些自訂的驗證器。

# 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()])

登入後複製
登入後複製

现在我们要添加我们的验证器用来确保它们提供的邮箱地址不存在数据库中。我们把这个验证器放在一个新的 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)

登入後複製

这个验证器假设我们是使用 SQLAlchemy 来定义我们的模型。WTForms 期待验证器返回某种可调用的对象(例如,一个可调用的类)。

在 Unique() 的 \_\_init\_\_ 中我们可以指定哪些参数传入到验证器中,在本例中我们要传入相关的模型(例如,在我们例子中是传入 User 模型)以及要检查的字段。当验证器被调用的时候,如果定义模型的任何实例匹配表单中提交的值,它将会抛出一个 ValidationError。我们也可以添加一个具有通用默认值的消息,它将会被包含在 ValidationError 中。

现在我们可以修改 EmailPasswordForm,使用我们自定义的 Unique 验证器。

# 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 也能帮助我们为表单渲染成 HTML 表示。WTForms 实现的 Field 字段能够渲染成该字段的 HTML 表示,所以为了渲染它们,我们只必须在我们模板中调用表单的字段。这就像渲染 csrf_token 字段。下面给出了一个登录模板的示例,在里面我们使用 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>

登入後複製

我们可以自定义如何渲染字段,通过传入字段的属性作为参数到调用中。

<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 登录
现实生活中,我们发现有很多人都不知道他们拥有一些公共账号。一部分大牌的网站或服务商都会为他们的会员提供公共账号的认证。举个栗子,如果你有一个 google 账号,其实你就有了一个公共账号,类似的还有 Yahoo, AOL, Flickr 等。
为了方便我们的用户能简单的使用他们的公共账号,我们将把这些公共账号的链接添加到一个列表,这样用户就不用自手工输入了。

我们要把一些提供给用户的公共账号服务商定义到一个列表里面,这个列表就放到配置文件中吧 (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' }]
登入後複製

接下来就是要在我们的登录视图函数中使用这个列表了:

@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 中引入了公共账号服务商的配置列表,然后把它作为一个参数通过 render_template 函数引入到模板。

接下来要做的我想你也猜得到,我们需要在登录模板中把这些服务商链接显示出来。

<!-- 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 %}
登入後複製

这次的模板添加的东西似乎有点多。一些公共账号需要提供用户名,为了解决这个我们用了点 javascript。当用户点击相关的公共账号链接时,需要用户名的公共账号会提示用户输入用户名, javascript 会把用户名处理成可用的公共账号,最后再插入到 openid 字段的文本框中。

下面这个是在登录页面点击 google 链接后显示的截图:

2016712172533772.jpg (450×300)

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1653
14
CakePHP 教程
1413
52
Laravel 教程
1306
25
PHP教程
1251
29
C# 教程
1224
24
PHP和Python:解釋了不同的範例 PHP和Python:解釋了不同的範例 Apr 18, 2025 am 12:26 AM

PHP主要是過程式編程,但也支持面向對象編程(OOP);Python支持多種範式,包括OOP、函數式和過程式編程。 PHP適合web開發,Python適用於多種應用,如數據分析和機器學習。

在PHP和Python之間進行選擇:指南 在PHP和Python之間進行選擇:指南 Apr 18, 2025 am 12:24 AM

PHP適合網頁開發和快速原型開發,Python適用於數據科學和機器學習。 1.PHP用於動態網頁開發,語法簡單,適合快速開發。 2.Python語法簡潔,適用於多領域,庫生態系統強大。

PHP和Python:深入了解他們的歷史 PHP和Python:深入了解他們的歷史 Apr 18, 2025 am 12:25 AM

PHP起源於1994年,由RasmusLerdorf開發,最初用於跟踪網站訪問者,逐漸演變為服務器端腳本語言,廣泛應用於網頁開發。 Python由GuidovanRossum於1980年代末開發,1991年首次發布,強調代碼可讀性和簡潔性,適用於科學計算、數據分析等領域。

Python vs. JavaScript:學習曲線和易用性 Python vs. JavaScript:學習曲線和易用性 Apr 16, 2025 am 12:12 AM

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

sublime怎麼運行代碼python sublime怎麼運行代碼python Apr 16, 2025 am 08:48 AM

在 Sublime Text 中運行 Python 代碼,需先安裝 Python 插件,再創建 .py 文件並編寫代碼,最後按 Ctrl B 運行代碼,輸出會在控制台中顯示。

vs code 可以在 Windows 8 中運行嗎 vs code 可以在 Windows 8 中運行嗎 Apr 15, 2025 pm 07:24 PM

VS Code可以在Windows 8上運行,但體驗可能不佳。首先確保系統已更新到最新補丁,然後下載與系統架構匹配的VS Code安裝包,按照提示安裝。安裝後,注意某些擴展程序可能與Windows 8不兼容,需要尋找替代擴展或在虛擬機中使用更新的Windows系統。安裝必要的擴展,檢查是否正常工作。儘管VS Code在Windows 8上可行,但建議升級到更新的Windows系統以獲得更好的開發體驗和安全保障。

vscode在哪寫代碼 vscode在哪寫代碼 Apr 15, 2025 pm 09:54 PM

在 Visual Studio Code(VSCode)中編寫代碼簡單易行,只需安裝 VSCode、創建項目、選擇語言、創建文件、編寫代碼、保存並運行即可。 VSCode 的優點包括跨平台、免費開源、強大功能、擴展豐富,以及輕量快速。

notepad 怎麼運行python notepad 怎麼運行python Apr 16, 2025 pm 07:33 PM

在 Notepad 中運行 Python 代碼需要安裝 Python 可執行文件和 NppExec 插件。安裝 Python 並為其添加 PATH 後,在 NppExec 插件中配置命令為“python”、參數為“{CURRENT_DIRECTORY}{FILE_NAME}”,即可在 Notepad 中通過快捷鍵“F6”運行 Python 代碼。

See all articles