sql >> データベース >  >> RDS >> Database

Flaskでの登録中の電子メール確認の処理

    このチュートリアルでは、ユーザー登録時にメールアドレスを検証する方法について詳しく説明します。

    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と呼びましょう。 -アプリケーションにアクセスする前に、アカウントを確認する必要があることをユーザーに通知します。」

    だから、私たちはする必要があります-

    1. /unconfirmedを追加します ルート
    2. unconfirmed.htmlを追加します テンプレート
    3. register()を更新します ビュー機能
    4. デコレータを作成する
    5. 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()
    

    次に、テストを再度実行します。すべて合格する必要があります!



    結論

    明らかに私たちにできることはもっとたくさんあります:

    1. リッチメールとプレーンテキストメール-両方を送信する必要があります。
    2. パスワードの電子メールをリセットする-パスワードを忘れたユーザーに送信する必要があります。
    3. ユーザー管理-ユーザーがメールアドレスとパスワードを更新できるようにする必要があります。メールアドレスが変更された場合は、再度確認する必要があります。
    4. テスト-新機能をカバーするために、さらにテストを作成する必要があります。

    Githubリポジトリからソースコード全体をダウンロードします。以下に質問をコメントしてください。パート2をご覧ください。

    ハッピーホリデー!



    1. AT TIME ZONE – SQLServer2016の新しいお気に入りの機能

    2. 1つのコマンドで行を選択または挿入する

    3. SQL Server2014CTP1でのパーティションレベルのオンラインインデックス操作の調査

    4. 高可用性のためにPostgreSQLをデプロイする方法