このチュートリアルでは、ユーザー登録時にメールアドレスを検証する方法について詳しく説明します。
2015年4月30日更新 :Python3のサポートを追加しました。
ワークフローに関しては、ユーザーが新しいアカウントを登録すると、確認メールが送信されます。ユーザーが電子メールの指示に従ってアカウントを「確認」するまで、ユーザーアカウントは「未確認」としてマークされます。これは、ほとんどのWebアプリケーションが従う単純なワークフローです。
考慮すべき重要なことの1つは、未確認のユーザーに許可されていることです。言い換えれば、彼らはあなたのアプリケーションへの完全なアクセス権、制限された/制限されたアクセス権、またはまったくアクセス権を持っていませんか?このチュートリアルのアプリケーションの場合、未確認のユーザーはログインできますが、アプリケーションにアクセスする前にアカウントを確認する必要があることを通知するページにすぐにリダイレクトされます。
始める前に、追加する機能のほとんどは、Flask-UserおよびFlask-Security拡張機能の一部です。これは、なぜ拡張機能を使用しないのかという疑問を投げかけます。さて、何よりもまず、これは学ぶ機会です。また、サポートされているデータベースのように、これらの拡張機能には両方とも制限があります。たとえば、RethinkDBを使用したい場合はどうなりますか?
始めましょう。
Flask基本登録
基本的なユーザー登録を含むFlaskボイラープレートから始めます。リポジトリからコードを取得します。 virtualenvを作成してアクティブ化したら、次のコマンドを実行してすぐに開始します。
$ pip install -r requirements.txt
$ export APP_SETTINGS="project.config.DevelopmentConfig"
$ python manage.py create_db
$ python manage.py db init
$ python manage.py db migrate
$ python manage.py create_admin
$ python manage.py runserver
詳細については、Readmeを確認してください。
アプリを実行した状態で、http:// localhost:5000 / registerに移動し、新しいユーザーを登録します。登録後、アプリが自動的にログインしてメインページにリダイレクトすることに注意してください。周りを見てから、コード、具体的には「ユーザー」の青写真を実行します。
完了したらサーバーを強制終了します。
現在のアプリを更新する
モデル
まず、confirmed
を追加しましょう User
へのフィールド project / models.pyのモデル :
class User(db.Model):
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String, unique=True, nullable=False)
password = db.Column(db.String, nullable=False)
registered_on = db.Column(db.DateTime, nullable=False)
admin = db.Column(db.Boolean, nullable=False, default=False)
confirmed = db.Column(db.Boolean, nullable=False, default=False)
confirmed_on = db.Column(db.DateTime, nullable=True)
def __init__(self, email, password, confirmed,
paid=False, admin=False, confirmed_on=None):
self.email = email
self.password = bcrypt.generate_password_hash(password)
self.registered_on = datetime.datetime.now()
self.admin = admin
self.confirmed = confirmed
self.confirmed_on = confirmed_on
このフィールドのデフォルトが「False」になっていることに注目してください。 confirmed_on
も追加しました [datetime
であるフィールド ](https://realpython.com/python-datetime/)。 registered_on
の違いを分析するために、このフィールドも含めるのが好きです。 およびconfirmed_on
コホート分析を使用した日付。
データベースと移行を完全にやり直しましょう。したがって、先に進んでデータベース dev.sqliteを削除します。 、および「migrations」フォルダ。
管理コマンド
次に、 manage.py内 、create_admin
を更新します 新しいデータベースフィールドを考慮に入れるコマンド:
@manager.command
def create_admin():
"""Creates the admin user."""
db.session.add(User(
email="[email protected]",
password="admin",
admin=True,
confirmed=True,
confirmed_on=datetime.datetime.now())
)
db.session.commit()
必ずdatetime
をインポートしてください 。次に、次のコマンドをもう一度実行します。
$ python manage.py create_db
$ python manage.py db init
$ python manage.py db migrate
$ python manage.py create_admin
register()
ビュー機能
最後に、ユーザーを再度登録する前に、register()
にすばやく変更を加える必要があります。 project / user / views.pyのビュー関数 …
変更:
user = User(
email=form.email.data,
password=form.password.data
)
宛先:
user = User(
email=form.email.data,
password=form.password.data,
confirmed=False
)
わかる?デフォルトのconfirmed
が必要な理由を考えてください False
へ 。
わかった。アプリを再度実行します。 http:// localhost:5000 / registerに移動して、新しいユーザーを再度登録します。 SQLiteブラウザでSQLiteデータベースを開くと、次のように表示されます。
それで、私が登録した新しいユーザー、[email protected]
、確認されていません。それを変えましょう。
確認メールを追加
確認トークンを生成する
確認メールには、ユーザーがアカウントを確認するためにクリックするだけの一意のURLが含まれている必要があります。理想的には、URLは次のようになります-http://yourapp.com/confirm/<id>
。ここで重要なのはid
です 。ユーザーの電子メールを(タイムスタンプとともに)id
にエンコードします 危険なパッケージを使用しています。
project / token.pyというファイルを作成します 次のコードを追加します:
# project/token.py
from itsdangerous import URLSafeTimedSerializer
from project import app
def generate_confirmation_token(email):
serializer = URLSafeTimedSerializer(app.config['SECRET_KEY'])
return serializer.dumps(email, salt=app.config['SECURITY_PASSWORD_SALT'])
def confirm_token(token, expiration=3600):
serializer = URLSafeTimedSerializer(app.config['SECRET_KEY'])
try:
email = serializer.loads(
token,
salt=app.config['SECURITY_PASSWORD_SALT'],
max_age=expiration
)
except:
return False
return email
したがって、generate_confirmation_token()
で URLSafeTimedSerializer
を使用する関数 ユーザー登録時に取得したメールアドレスを使用してトークンを生成します。 実際 電子メールはトークンにエンコードされます。次に、トークンを確認するために、confirm_token()
内で 関数では、loads()
を使用できます メソッド。トークンと有効期限(1時間(3,600秒)有効)を引数として受け取ります。トークンの有効期限が切れていない限り、メールが返されます。
必ずSECURITY_PASSWORD_SALT
を追加してください アプリの構成に(BaseConfig()
):
SECURITY_PASSWORD_SALT = 'my_precious_two'
register()
を更新します ビュー機能
それでは、register()
を更新しましょう。 project / user / views.pyから関数を再度表示します :
@user_blueprint.route('/register', methods=['GET', 'POST'])
def register():
form = RegisterForm(request.form)
if form.validate_on_submit():
user = User(
email=form.email.data,
password=form.password.data,
confirmed=False
)
db.session.add(user)
db.session.commit()
token = generate_confirmation_token(user.email)
また、インポートを更新してください:
from project.token import generate_confirmation_token, confirm_token
メール確認の処理
次に、メール確認を処理するための新しいビューを追加しましょう:
@user_blueprint.route('/confirm/<token>')
@login_required
def confirm_email(token):
try:
email = confirm_token(token)
except:
flash('The confirmation link is invalid or has expired.', 'danger')
user = User.query.filter_by(email=email).first_or_404()
if user.confirmed:
flash('Account already confirmed. Please login.', 'success')
else:
user.confirmed = True
user.confirmed_on = datetime.datetime.now()
db.session.add(user)
db.session.commit()
flash('You have confirmed your account. Thanks!', 'success')
return redirect(url_for('main.home'))
これをproject/ user / views.pyに追加します 。また、インポートを更新してください:
import datetime
ここでは、confirm_token()
を呼び出します。 関数、トークンを渡します。成功した場合は、ユーザーを更新し、email_confirmed
を変更します True
の属性 datetime
を設定します 確認が行われたときのために。また、ユーザーがすでに確認プロセスを完了し、確認された場合は、これをユーザーに警告します。
メールテンプレートを作成する
次に、基本のメールテンプレートを追加しましょう:
<p>Welcome! Thanks for signing up. Please follow this link to activate your account:</p>
<p><a href="{{ confirm_url }}">{{ confirm_url }}</a></p>
<br>
<p>Cheers!</p>
これをactivate.htmlとして保存します 「プロジェクト/テンプレート/ユーザー」で。これはconfirm_url
と呼ばれる単一の変数を取ります 、register()
で作成されます 表示機能。
メールを送信
project/__init__.py
にすでにインストールおよび設定されているFlask-Mailの助けを借りて、メールを送信するための基本的な関数を作成しましょう。 。
email.pyというファイルを作成します :
# project/email.py
from flask.ext.mail import Message
from project import app, mail
def send_email(to, subject, template):
msg = Message(
subject,
recipients=[to],
html=template,
sender=app.config['MAIL_DEFAULT_SENDER']
)
mail.send(msg)
これを「プロジェクト」フォルダに保存します。
したがって、受信者のリスト、件名、およびテンプレートを渡すだけで済みます。メール設定の設定については後ほど説明します。
register()
を更新します project / user / views.pyのビュー関数(再び!)
@user_blueprint.route('/register', methods=['GET', 'POST'])
def register():
form = RegisterForm(request.form)
if form.validate_on_submit():
user = User(
email=form.email.data,
password=form.password.data,
confirmed=False
)
db.session.add(user)
db.session.commit()
token = generate_confirmation_token(user.email)
confirm_url = url_for('user.confirm_email', token=token, _external=True)
html = render_template('user/activate.html', confirm_url=confirm_url)
subject = "Please confirm your email"
send_email(user.email, subject, html)
login_user(user)
flash('A confirmation email has been sent via email.', 'success')
return redirect(url_for("main.home"))
return render_template('user/register.html', form=form)
次のインポートも追加します:
from project.email import send_email
ここでは、すべてをまとめています。この機能は基本的に、プロセス全体のコントローラーとして(直接的または間接的に)機能します。
- 初期登録を処理します
- トークンと確認URLを生成します
- 確認メールを送信します
- フラッシュ確認
- ユーザーにログインし、
- ユーザーをリダイレクトします。
_external=True
に気づきましたか 口論?これにより、ホスト名とポート(この場合はhttp:// localhost:5000)を含む完全な絶対URLが追加されます。
これをテストする前に、メール設定をセットアップする必要があります。
メール
BaseConfig()
を更新することから始めます project / config.py内 :
class BaseConfig(object):
"""Base configuration."""
# main config
SECRET_KEY = 'my_precious'
SECURITY_PASSWORD_SALT = 'my_precious_two'
DEBUG = False
BCRYPT_LOG_ROUNDS = 13
WTF_CSRF_ENABLED = True
DEBUG_TB_ENABLED = False
DEBUG_TB_INTERCEPT_REDIRECTS = False
# mail settings
MAIL_SERVER = 'smtp.googlemail.com'
MAIL_PORT = 465
MAIL_USE_TLS = False
MAIL_USE_SSL = True
# gmail authentication
MAIL_USERNAME = os.environ['APP_MAIL_USERNAME']
MAIL_PASSWORD = os.environ['APP_MAIL_PASSWORD']
# mail accounts
MAIL_DEFAULT_SENDER = '[email protected]'
詳細については、Flask-Mailの公式ドキュメントを確認してください。
すでにGMAILアカウントをお持ちの場合は、それを使用するか、テスト用のGMAILアカウントを登録できます。次に、現在のシェルセッションで環境変数を一時的に設定します。
$ export APP_MAIL_USERNAME="foo"
$ export APP_MAIL_PASSWORD="bar"
GMAILアカウントに2段階認証がある場合、Googleはその試行をブロックします。
では、テストしましょう!
最初のテスト
アプリを起動し、http:// localhost:5000/registerに移動します。次に、アクセスできるメールアドレスで登録します。すべてがうまくいけば、受信トレイに次のようなメールが届くはずです。
URLをクリックすると、http:// localhost:5000/に移動します。ユーザーがデータベースに存在し、「確認済み」フィールドがTrue
であることを確認してください 、およびdatetime
があります confirmed_on
に関連付けられています フィールド。
いいね!
権限の処理
このチュートリアルの冒頭で、「未確認のユーザーはログインできますが、すぐにページにリダイレクトする必要があります。ルートを/unconfirmed
と呼びましょう。 -アプリケーションにアクセスする前に、アカウントを確認する必要があることをユーザーに通知します。」
だから、私たちはする必要があります-
-
/unconfirmed
を追加します ルート - unconfirmed.htmlを追加します テンプレート
-
register()
を更新します ビュー機能 - デコレータを作成する
- Navigation.htmlを更新します テンプレート
/unconfirmed
を追加します ルート
次のルートをproject/ user / views.pyに追加します :
@user_blueprint.route('/unconfirmed')
@login_required
def unconfirmed():
if current_user.confirmed:
return redirect('main.home')
flash('Please confirm your account!', 'warning')
return render_template('user/unconfirmed.html')
以前に同様のコードを見たことがあるので、次に進みましょう。
unconfirmed.htmlを追加します テンプレート
{% extends "_base.html" %}
{% block content %}
<h1>Welcome!</h1>
<br>
<p>You have not confirmed your account. Please check your inbox (and your spam folder) - you should have received an email with a confirmation link.</p>
<p>Didn't get the email? <a href="/">Resend</a>.</p>
{% endblock %}
これをunconfirmed.htmlとして保存します 「プロジェクト/テンプレート/ユーザー」で。繰り返しますが、これはすべて簡単なはずです。今のところ、確認メールを再送信するためのダミーURLを追加しました。これについては、さらに詳しく説明します。
register()
を更新します ビュー機能
変更するだけです:
return redirect(url_for("main.home"))
宛先:
return redirect(url_for("user.unconfirmed"))
そのため、確認メールが送信された後、ユーザーは/unconfirmed
にリダイレクトされます。 ルート。
デコレータを作成する
# project/decorators.py
from functools import wraps
from flask import flash, redirect, url_for
from flask.ext.login import current_user
def check_confirmed(func):
@wraps(func)
def decorated_function(*args, **kwargs):
if current_user.confirmed is False:
flash('Please confirm your account!', 'warning')
return redirect(url_for('user.unconfirmed'))
return func(*args, **kwargs)
return decorated_function
ここに、ユーザーが未確認かどうかをチェックする基本的な機能があります。未確認の場合、ユーザーは/unconfirmed
にリダイレクトされます ルート。これをdecorators.pyとして保存します 「プロジェクト」ディレクトリにあります。
次に、profile()
を飾ります 表示機能:
@user_blueprint.route('/profile', methods=['GET', 'POST'])
@login_required
@check_confirmed
def profile():
# ... snip ...
デコレータをインポートしてください:
from project.decorators import check_confirmed
Navigation.htmlを更新します テンプレート
最後に、 Navigation.htmlの次の部分を更新します テンプレート-
変更:
<ul class="nav navbar-nav">
{% if current_user.is_authenticated() %}
<li><a href="{{ url_for('user.profile') }}">Profile</a></li>
{% endif %}
</ul>
宛先:
<ul class="nav navbar-nav">
{% if current_user.confirmed and current_user.is_authenticated() %}
<li><a href="{{ url_for('user.profile') }}">Profile</a></li>
{% elif current_user.is_authenticated() %}
<li><a href="{{ url_for('user.unconfirmed') }}">Confirm</a></li>
{% endif %}
</ul>
もう一度テストしましょう!
2番目のテスト
アプリを起動し、アクセスできるメールアドレスで再登録します。 (最初に登録した古いユーザーをデータベースから削除して、再度使用してください。)これで、登録後にhttp:// localhost:5000/unconfirmedにリダイレクトされます。
必ずhttp:// localhost:5000/profileルートをテストしてください。これにより、http:// localhost:5000/unconfirmedにリダイレクトされます。
先に進んでメールを確認すると、すべてのページにアクセスできるようになります。ブーム!
メールを再送信
最後に、再送リンクを機能させましょう。次のビュー関数をproject/ user / views.pyに追加します :
@user_blueprint.route('/resend')
@login_required
def resend_confirmation():
token = generate_confirmation_token(current_user.email)
confirm_url = url_for('user.confirm_email', token=token, _external=True)
html = render_template('user/activate.html', confirm_url=confirm_url)
subject = "Please confirm your email"
send_email(current_user.email, subject, html)
flash('A new confirmation email has been sent.', 'success')
return redirect(url_for('user.unconfirmed'))
次に、 unconfirmed.htmlを更新します テンプレート:
{% extends "_base.html" %}
{% block content %}
<h1>Welcome!</h1>
<br>
<p>You have not confirmed your account. Please check your inbox (and your spam folder) - you should have received an email with a confirmation link.</p>
<p>Didn't get the email? <a href="{{ url_for('user.resend_confirmation') }}">Resend</a>.</p>
{% endblock %}
3番目のテスト
あなたはドリルを知っています。今回は、必ず新しい確認メールを再送信して、リンクをテストしてください。動作するはずです。
最後に、確認リンクをいくつか送信するとどうなりますか?それぞれ有効ですか?それをテストします。新しいユーザーを登録してから、いくつかの新しい確認メールを送信します。最初のメールで確認してみてください。うまくいきましたか?そうすべき。これで大丈夫ですか?新しいメールが送信された場合、他のメールは期限切れになると思いますか?
これについていくつかの調査を行います。そして、使用している他のWebアプリケーションをテストします。彼らはそのような行動をどのように処理しますか?
テストスイートの更新
大丈夫。主な機能は以上です。現在のテストスイートは壊れているので、更新してはどうでしょうか。
テストを実行します:
$ python manage.py test
次のエラーが表示されます:
TypeError: __init__() takes at least 4 arguments (3 given)
これを修正するには、setUp()
を更新する必要があります project / util.pyのメソッド :
def setUp(self):
db.create_all()
user = User(email="[email protected]", password="admin_user", confirmed=False)
db.session.add(user)
db.session.commit()
次に、テストを再度実行します。すべて合格する必要があります!
結論
明らかに私たちにできることはもっとたくさんあります:
- リッチメールとプレーンテキストメール-両方を送信する必要があります。
- パスワードの電子メールをリセットする-パスワードを忘れたユーザーに送信する必要があります。
- ユーザー管理-ユーザーがメールアドレスとパスワードを更新できるようにする必要があります。メールアドレスが変更された場合は、再度確認する必要があります。
- テスト-新機能をカバーするために、さらにテストを作成する必要があります。
Githubリポジトリからソースコード全体をダウンロードします。以下に質問をコメントしてください。パート2をご覧ください。
ハッピーホリデー!