Database
 sql >> Teknologi Basis Data >  >> RDS >> Database

Menangani Konfirmasi Email Saat Registrasi di Flask

Tutorial ini menjelaskan cara memvalidasi alamat email selama pendaftaran pengguna.

Diperbarui 30/04/2015 :Menambahkan dukungan Python 3.

Dalam hal alur kerja, setelah pengguna mendaftarkan akun baru, email konfirmasi dikirim. Akun pengguna ditandai sebagai "belum dikonfirmasi" sampai pengguna, yah, "mengkonfirmasi" akun melalui instruksi di email. Ini adalah alur kerja sederhana yang diikuti sebagian besar aplikasi web.

Satu hal penting untuk dipertimbangkan adalah apa yang boleh dilakukan oleh pengguna yang belum dikonfirmasi. Dengan kata lain, apakah mereka memiliki akses penuh ke aplikasi Anda, akses terbatas/terbatas, atau tidak ada akses sama sekali? Untuk aplikasi dalam tutorial ini, pengguna yang belum dikonfirmasi dapat masuk tetapi mereka segera diarahkan ke halaman yang mengingatkan mereka bahwa mereka perlu mengonfirmasi akun mereka sebelum dapat mengakses aplikasi.

Sebelum memulai, sebagian besar fungsi yang akan kami tambahkan adalah bagian dari ekstensi Flask-User dan Flask-Security - yang menimbulkan pertanyaan mengapa tidak menggunakan ekstensi saja? Yah, pertama dan terpenting, ini adalah kesempatan untuk belajar. Selain itu, kedua ekstensi tersebut memiliki keterbatasan, seperti database yang didukung. Bagaimana jika Anda ingin menggunakan RethinkDB, misalnya?

Mari kita mulai.


Pendaftaran dasar labu

Kami akan mulai dengan boilerplate Flask yang mencakup pendaftaran pengguna dasar. Ambil kode dari repositori. Setelah Anda membuat dan mengaktifkan virtualenv, jalankan perintah berikut untuk memulai dengan cepat:

$ 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

Lihat readme untuk informasi lebih lanjut.

Dengan aplikasi berjalan, navigasikan ke http://localhost:5000/register dan daftarkan pengguna baru. Perhatikan bahwa setelah pendaftaran, aplikasi secara otomatis memasukkan Anda dan mengarahkan Anda ke halaman utama. Lihat-lihat, lalu jalankan kodenya - khususnya cetak biru “pengguna”.

Matikan server setelah selesai.



Perbarui aplikasi saat ini


Model

Pertama, mari tambahkan confirmed ke User . kami model di 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

Perhatikan bagaimana bidang ini default ke 'False'. Kami juga menambahkan confirmed_on bidang, yang merupakan [datetime ] (https://realpython.com/python-datetime/). Saya ingin memasukkan bidang ini juga untuk menganalisis perbedaan antara registered_on dan confirmed_on tanggal menggunakan analisis kohort.

Mari kita mulai dari awal lagi dengan database dan migrasi kita. Jadi, lanjutkan dan hapus database, dev.sqlite , serta folder “migrasi”.



Kelola perintah

Selanjutnya, dalam manage.py , perbarui create_admin perintah untuk mempertimbangkan bidang database baru:

@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()

Pastikan untuk mengimpor datetime . Sekarang, lanjutkan dan jalankan kembali perintah berikut:

$ python manage.py create_db
$ python manage.py db init
$ python manage.py db migrate
$ python manage.py create_admin


register() fungsi tampilan

Terakhir, sebelum kita dapat mendaftarkan pengguna lagi, kita perlu membuat perubahan cepat pada register() lihat fungsi di project/user/views.py

Ubah:

user = User(
    email=form.email.data,
    password=form.password.data
)

Kepada:

user = User(
    email=form.email.data,
    password=form.password.data,
    confirmed=False
)

Masuk akal? Pikirkan tentang mengapa kami ingin default confirmed menjadi False .

Oke. Jalankan aplikasi lagi. Arahkan ke http://localhost:5000/register dan daftarkan pengguna baru lagi. Jika Anda membuka database SQLite di Browser SQLite, Anda akan melihat:

Jadi, pengguna baru yang saya daftarkan, [email protected] , tidak dikonfirmasi. Mari kita ubah itu.




Tambahkan konfirmasi email


Buat token konfirmasi

Konfirmasi email harus berisi URL unik yang hanya perlu diklik oleh pengguna untuk mengonfirmasi akunnya. Idealnya, URL akan terlihat seperti ini - http://yourapp.com/confirm/<id> . Kuncinya di sini adalah id . Kami akan mengkodekan email pengguna (bersama dengan stempel waktu) di id menggunakan paket berbahayanya.

Buat file bernama project/token.py dan tambahkan kode berikut:

# 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

Jadi, di generate_confirmation_token() fungsi kami menggunakan URLSafeTimedSerializer untuk menghasilkan token menggunakan alamat email yang diperoleh saat pendaftaran pengguna. sebenarnya email dikodekan dalam token. Kemudian untuk mengonfirmasi token, di dalam confirm_token() fungsi, kita dapat menggunakan loads() metode, yang menggunakan token dan kedaluwarsa - berlaku selama satu jam (3.600 detik) - sebagai argumen. Selama token belum kedaluwarsa, maka akan mengembalikan email.

Pastikan untuk menambahkan SECURITY_PASSWORD_SALT ke konfigurasi aplikasi Anda (BaseConfig() ):

SECURITY_PASSWORD_SALT = 'my_precious_two'


Perbarui register() fungsi tampilan

Sekarang mari kita perbarui register() lihat fungsi lagi dari 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)

Juga, pastikan untuk memperbarui impor:

from project.token import generate_confirmation_token, confirm_token


Menangani Konfirmasi Email

Selanjutnya, mari tambahkan tampilan baru untuk menangani konfirmasi email:

@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'))

Tambahkan ini ke project/user/views.py . Juga, pastikan untuk memperbarui impor:

import datetime

Di sini, kita memanggil confirm_token() fungsi, meneruskan token. Jika berhasil, kami memperbarui pengguna, mengubah email_confirmed atribut ke True dan mengatur datetime untuk saat konfirmasi terjadi. Selain itu, jika pengguna telah melalui proses konfirmasi - dan telah dikonfirmasi - maka kami akan memperingatkan pengguna tentang hal ini.



Buat template email

Selanjutnya, mari tambahkan template email dasar:

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

Simpan ini sebagai activate.html di "proyek/templat/pengguna". Ini mengambil satu variabel yang disebut confirm_url , yang akan dibuat di register() fungsi tampilan.



Kirim email

Mari kita buat fungsi dasar untuk mengirim email dengan sedikit bantuan dari Flask-Mail, yang sudah terinstal dan setup di project/__init__.py .

Buat file bernama 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)

Simpan ini di folder "proyek".

Jadi, kita hanya perlu memberikan daftar penerima, subjek, dan template. Kami akan membahas pengaturan konfigurasi email sebentar lagi.



Perbarui register() lihat fungsi di project/user/views.py (lagi!)

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

Tambahkan juga impor berikut:

from project.email import send_email

Di sini, kami menggabungkan semuanya. Fungsi ini pada dasarnya bertindak sebagai pengontrol (baik secara langsung maupun tidak langsung) untuk seluruh proses:

  • Menangani pendaftaran awal,
  • Buat token dan URL konfirmasi,
  • Kirim email konfirmasi,
  • Konfirmasi flash,
  • Masukkan pengguna, dan
  • Mengalihkan pengguna.

Apakah Anda memperhatikan _external=True argumen? Ini menambahkan URL absolut lengkap yang menyertakan nama host dan port (http://localhost:5000, dalam kasus kami.)

Sebelum kita dapat mengujinya, kita perlu mengatur pengaturan email kita.



Surat

Mulailah dengan memperbarui BaseConfig() di 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]'

Lihat dokumentasi Flask-Mail resmi untuk info lebih lanjut.

Jika Anda sudah memiliki akun GMAIL maka Anda dapat menggunakannya atau mendaftarkan akun GMAIL percobaan. Kemudian atur variabel lingkungan sementara di sesi shell saat ini:

$ export APP_MAIL_USERNAME="foo"
$ export APP_MAIL_PASSWORD="bar"

Jika akun GMAIL Anda memiliki autentikasi 2 langkah, Google akan memblokir upaya tersebut.

Sekarang mari kita uji!




Tes pertama

Jalankan aplikasi, dan navigasikan ke http://localhost:5000/register. Kemudian daftar dengan alamat email yang dapat Anda akses. Jika semuanya berjalan dengan baik, Anda akan memiliki email di kotak masuk Anda yang terlihat seperti ini:

Klik URL dan Anda akan dibawa ke http://localhost:5000/. Pastikan bahwa pengguna ada di database, bidang 'dikonfirmasi' adalah True , dan ada datetime terkait dengan confirmed_on lapangan.

Bagus!



Menangani izin

Jika Anda ingat, di awal tutorial ini, kami memutuskan bahwa “pengguna yang belum dikonfirmasi dapat masuk tetapi mereka harus segera diarahkan ke halaman - sebut saja rute /unconfirmed - mengingatkan pengguna bahwa mereka perlu mengonfirmasi akun mereka sebelum dapat mengakses aplikasi”.

Jadi, kita perlu-

  1. Tambahkan /unconfirmed rute
  2. Tambahkan unconfirmed.html templat
  3. Perbarui register() fungsi tampilan
  4. Buat dekorator
  5. Perbarui navigation.html templat

Tambahkan /unconfirmed rute

Tambahkan rute berikut ke 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')

Anda pernah melihat kode serupa sebelumnya, jadi mari kita lanjutkan.



Tambahkan unconfirmed.html templat

{% 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 %}

Simpan ini sebagai unconfirmed.html di "proyek/templat/pengguna". Sekali lagi, ini semua harus langsung. Untuk saat ini, kami baru saja menambahkan URL tiruan untuk mengirim ulang email konfirmasi. Kami akan membahas ini lebih jauh.



Perbarui register() fungsi tampilan

Sekarang cukup ubah:

return redirect(url_for("main.home"))

Kepada:

return redirect(url_for("user.unconfirmed"))

Jadi, setelah email konfirmasi terkirim, pengguna kini dialihkan ke kode /unconfirmed rute.



Buat dekorator

# 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

Di sini kami memiliki fungsi dasar untuk memeriksa apakah pengguna belum dikonfirmasi. Jika belum dikonfirmasi, pengguna diarahkan ke /unconfirmed rute. Simpan ini sebagai decorators.py di direktori “proyek”.

Sekarang, hiasi profile() fungsi tampilan:

@user_blueprint.route('/profile', methods=['GET', 'POST'])
@login_required
@check_confirmed
def profile():
    # ... snip ...

Pastikan untuk mengimpor dekorator:

from project.decorators import check_confirmed


Perbarui navigation.html templat

Terakhir, perbarui bagian navigation.html berikut ini template-

Ubah:

<ul class="nav navbar-nav">
  {% if current_user.is_authenticated() %}
    <li><a href="{{ url_for('user.profile') }}">Profile</a></li>
  {% endif %}
</ul>

Kepada:

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

Saatnya menguji lagi!




Ujian kedua

Jalankan aplikasi, dan daftar lagi dengan alamat email yang dapat Anda akses. (Jangan ragu untuk menghapus pengguna lama yang Anda daftarkan sebelumnya dari database untuk digunakan kembali.) Sekarang Anda harus diarahkan ke http://localhost:5000/unconfirmed setelah pendaftaran.

Pastikan untuk menguji rute http://localhost:5000/profile. Ini akan mengarahkan Anda ke http://localhost:5000/unconfirmed.

Silakan dan konfirmasi email, dan Anda akan memiliki akses ke semua halaman. Boom!



Kirim ulang email

Akhirnya, mari buat tautan kirim ulang berfungsi. Tambahkan fungsi tampilan berikut ke 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'))

Sekarang perbarui unconfirmed.html templat:

{% 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 %}


Tes ketiga

Kamu tahu latihannya. Kali ini pastikan untuk mengirim ulang email konfirmasi baru dan menguji tautannya. Seharusnya berhasil.

Terakhir, apa yang terjadi jika Anda mengirim sendiri beberapa tautan konfirmasi? Apakah masing-masing valid? Ujilah. Daftarkan pengguna baru, lalu kirim beberapa email konfirmasi baru. Coba konfirmasi dengan email pertama. Apa itu bekerja? Itu harus. Apakah ini baik? Apakah menurut Anda email lain tersebut akan kedaluwarsa jika yang baru dikirim?

Lakukan penelitian tentang ini. Dan uji aplikasi web lain yang Anda gunakan. Bagaimana mereka menangani perilaku seperti itu?



Perbarui rangkaian pengujian

Baiklah. Jadi itu saja untuk fungsi utama. Bagaimana kalau kita memperbarui rangkaian pengujian saat ini karena rusak.

Jalankan tes:

$ python manage.py test

Anda akan melihat kesalahan berikut:

TypeError: __init__() takes at least 4 arguments (3 given)

Untuk memperbaikinya, kita hanya perlu memperbarui setUp() metode di 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()

Sekarang jalankan tes lagi. Semua harus lulus!



Kesimpulan

Jelas ada banyak lagi yang bisa kita lakukan:

  1. Email kaya vs. teks biasa - Kami harus mengirimkan keduanya.
  2. Setel ulang email sandi - Email ini harus dikirimkan kepada pengguna yang lupa sandi.
  3. Pengelolaan pengguna - Kami harus mengizinkan pengguna untuk memperbarui email dan sandi mereka, dan saat email diubah, email tersebut harus dikonfirmasi lagi.
  4. Pengujian - Kami perlu menulis lebih banyak pengujian untuk mencakup fitur-fitur baru.

Unduh seluruh kode sumber dari repositori Github. Komentar di bawah dengan pertanyaan. Lihat bagian 2.

Selamat berlibur!



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Hubungkan Aplikasi ODBC di Windows ke QuickBooks Online

  2. Cara Mengekstrak Substring Dari String di T-SQL

  3. Cara Mengubah Kolom Dari NULL menjadi NOT NULL

  4. SQL Antar Operator

  5. DMS Layanan Migrasi Database AWS