sql >> Databáze >  >> RDS >> Database

Manipulace s potvrzením e-mailu během registrace v baňce

Tento výukový program podrobně popisuje, jak ověřit e-mailové adresy během registrace uživatele.

Aktualizováno 30. 4. 2015 :Přidána podpora Pythonu 3.

Pokud jde o pracovní postup, poté, co si uživatel zaregistruje nový účet, je odeslán potvrzovací e-mail. Uživatelský účet je označen jako „nepotvrzený“, dokud uživatel „nepotvrdí“ účet prostřednictvím pokynů v e-mailu. Toto je jednoduchý pracovní postup, kterým se řídí většina webových aplikací.

Jedna důležitá věc, kterou je třeba vzít v úvahu, je to, co mohou nepotvrzení uživatelé dělat. Jinými slovy, mají úplný přístup k vaší aplikaci, omezený/omezený přístup nebo vůbec žádný? Pro aplikaci v tomto tutoriálu se mohou nepotvrzení uživatelé přihlásit, ale budou okamžitě přesměrováni na stránku, která jim připomene, že před přístupem k aplikaci musí potvrdit svůj účet.

Než začneme, většina funkcí, které budeme přidávat, je součástí rozšíření Flask-User a Flask-Security – což vyvolává otázku, proč nepoužít pouze rozšíření? No, v první řadě je to příležitost se učit. Obě tato rozšíření mají také omezení, stejně jako podporované databáze. Co kdybyste chtěli použít například RethinkDB?

Začněme.


Základní registrace baňky

Začneme základním vzorem Flask, který zahrnuje základní registraci uživatele. Získejte kód z úložiště. Jakmile vytvoříte a aktivujete virtualenv, spusťte následující příkazy, abyste mohli rychle začít:

$ 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

Další informace naleznete v souboru readme.

Se spuštěnou aplikací přejděte na http://localhost:5000/register a zaregistrujte nového uživatele. Všimněte si, že po registraci vás aplikace automaticky přihlásí a přesměruje na hlavní stránku. Rozhlédněte se a poté projděte kód – konkrétně „uživatelský“ plán.

Až budete hotovi, zabijte server.



Aktualizovat aktuální aplikaci


Modely

Nejprve přidejte confirmed pole našemu User model v 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

Všimněte si, že toto pole má výchozí hodnotu „False“. Také jsme přidali confirmed_on pole, což je [datetime ] (https://realpython.com/python-datetime/). Rád bych zahrnul i toto pole, abych analyzoval rozdíl mezi registered_on a confirmed_on data pomocí kohortové analýzy.

Začněme úplně znovu s naší databází a migrací. Takže pokračujte a odstraňte databázi dev.sqlite a také složku „migrations“.



Spravovat příkaz

Dále v rámci manage.py , aktualizujte create_admin příkaz k zohlednění nových databázových polí:

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

Nezapomeňte importovat datetime . Nyní pokračujte a znovu spusťte následující příkazy:

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


register() funkce zobrazení

Nakonec, než budeme moci znovu zaregistrovat uživatele, musíme provést rychlou změnu v register() funkce zobrazení v projekt/uživatel/views.py

Změna:

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

Komu:

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

Dávat smysl? Přemýšlejte o tom, proč bychom chtěli jako výchozí nastavit confirmed na False .

Dobře. Spusťte aplikaci znovu. Přejděte na http://localhost:5000/register a znovu zaregistrujte nového uživatele. Pokud otevřete databázi SQLite v prohlížeči SQLite, měli byste vidět:

Takže nový uživatel, kterého jsem zaregistroval, [email protected] , není potvrzeno. Pojďme to změnit.




Přidat potvrzení e-mailem


Vygenerovat potvrzovací token

Potvrzení e-mailu by mělo obsahovat jedinečnou adresu URL, na kterou uživatel jednoduše klikne, aby potvrdil svůj účet. V ideálním případě by adresa URL měla vypadat nějak takto – http://yourapp.com/confirm/<id> . Klíčem je zde id . E-mail uživatele (spolu s časovým razítkem) zakódujeme do id pomocí jeho nebezpečného balíčku.

Vytvořte soubor s názvem project/token.py a přidejte následující kód:

# 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

Takže v generate_confirmation_token() používáme funkci URLSafeTimedSerializer k vygenerování tokenu pomocí e-mailové adresy získané při registraci uživatele. skutečné e-mail je zakódován v tokenu. Poté potvrďte token v confirm_token() můžeme použít funkci loads() metoda, která jako argumenty bere token a expiraci - platnou jednu hodinu (3 600 sekund). Dokud token nevypršel, vrátí e-mail.

Nezapomeňte přidat SECURITY_PASSWORD_SALT do konfigurace vaší aplikace (BaseConfig() ):

SECURITY_PASSWORD_SALT = 'my_precious_two'


Aktualizujte register() funkce zobrazení

Nyní aktualizujme register() funkci zobrazení znovu z projekt/uživatel/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)

Nezapomeňte také aktualizovat importy:

from project.token import generate_confirmation_token, confirm_token


Zpracování potvrzení e-mailem

Dále přidáme nové zobrazení pro zpracování e-mailového potvrzení:

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

Přidejte to do project/user/views.py . Nezapomeňte také aktualizovat importy:

import datetime

Zde voláme confirm_token() funkce, předávání tokenu. Pokud bude úspěšná, aktualizujeme uživatele a změníme email_confirmed atribut True a nastavení datetime za kdy došlo k potvrzení. Také v případě, že uživatel již prošel potvrzovacím procesem – a je potvrzen – na to uživatele upozorníme.



Vytvořte šablonu e-mailu

Dále přidáme základní šablonu e-mailu:

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

Uložte to jako activate.html v „projekt/šablony/uživatel“. To trvá jednu proměnnou s názvem confirm_url , který bude vytvořen v register() funkce zobrazení.



Odeslat e-mail

Pojďme vytvořit základní funkci pro odesílání e-mailů s malou pomocí od Flask-Mail, který je již nainstalován a nastaven v project/__init__.py .

Vytvořte soubor s názvem 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)

Uložte to do složky „project“.

Takže musíme jednoduše předat seznam příjemců, předmět a šablonu. Za chvíli se budeme zabývat nastavením konfigurace pošty.



Aktualizujte register() funkce zobrazení v project/user/views.py (opět!)

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

Přidejte také následující import:

from project.email import send_email

Tady dáváme vše dohromady. Tato funkce v podstatě funguje jako regulátor (ať už přímo nebo nepřímo) celého procesu:

  • Zvládněte počáteční registraci,
  • Vygenerujte token a potvrzovací adresu URL,
  • Odeslat potvrzovací e-mail,
  • Potvrzení Flash,
  • Přihlaste se uživatele a
  • Přesměrovat uživatele.

Všimli jste si _external=True argument? Tím se přidá úplná absolutní adresa URL, která zahrnuje název hostitele a port (v našem případě http://localhost:5000.)

Než to budeme moci vyzkoušet, musíme nastavit naše nastavení pošty.



Pošta

Začněte aktualizací BaseConfig() v 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]'

Podívejte se na oficiální dokumentaci Flask-Mail pro více informací.

Pokud již máte účet GMAIL, můžete jej použít nebo si zaregistrovat testovací účet GMAIL. Poté dočasně nastavte proměnné prostředí v aktuální relaci shellu:

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

Pokud má váš účet GMAIL dvoufázové ověření, Google pokus zablokuje.

Nyní pojďme testovat!




První test

Spusťte aplikaci a přejděte na http://localhost:5000/register. Poté se zaregistrujte pomocí e-mailové adresy, ke které máte přístup. Pokud vše proběhlo v pořádku, měli byste mít ve své doručené poště e-mail, který vypadá asi takto:

Klikněte na URL a měli byste být přesměrováni na http://localhost:5000/. Ujistěte se, že uživatel je v databázi, pole „potvrzeno“ je True a je tam datetime spojené s confirmed_on pole.

Pěkné!



Zpracování oprávnění

Pokud si vzpomínáte, na začátku tohoto tutoriálu jsme se rozhodli, že „nepotvrzení uživatelé se mohou přihlásit, ale měli by být okamžitě přesměrováni na stránku – nazvěme cestu /unconfirmed - připomenutí uživatelům, že před přístupem k aplikaci musí potvrdit svůj účet.“

Takže musíme-

  1. Přidejte /unconfirmed trasa
  2. Přidejte unconfirmed.html šablona
  3. Aktualizujte register() funkce zobrazení
  4. Vytvořte dekoratér
  5. Aktualizujte navigation.html šablona

Přidat /unconfirmed trasa

Přidejte následující trasu do 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')

Podobný kód jste již viděli, takže pojďme dál.



Přidat unconfirmed.html šablona

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

Uložte to jako unconfirmed.html v „projekt/šablony/uživatel“. Opět by to vše mělo být jednoduché. Prozatím jsme přidali fiktivní adresu URL pro opětovné zaslání potvrzovacího e-mailu. Tomu se budeme věnovat dále.



Aktualizujte register() funkce zobrazení

Nyní jednoduše změňte:

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

Komu:

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

Po odeslání potvrzovacího e-mailu je uživatel nyní přesměrován na /unconfirmed trasa.



Vytvořte dekoratér

# 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

Zde máme základní funkci pro kontrolu, zda uživatel není potvrzen. Pokud není potvrzeno, je uživatel přesměrován na /unconfirmed trasa. Uložte to jako decorators.py v adresáři „project“.

Nyní ozdobte profile() funkce zobrazení:

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

Nezapomeňte importovat dekoratér:

from project.decorators import check_confirmed


Aktualizujte navigation.html šablona

Nakonec aktualizujte následující část navigation.html šablona-

Změna:

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

Komu:

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

Je čas znovu otestovat!




Druhý test

Spusťte aplikaci a znovu se zaregistrujte pomocí e-mailové adresy, ke které máte přístup. (Neváhejte a vymažte starého uživatele, kterého jste předtím zaregistrovali, z databáze, abyste jej mohli znovu použít.) Nyní byste měli být po registraci přesměrováni na http://localhost:5000/unconfirmed.

Ujistěte se, že jste otestovali cestu http://localhost:5000/profile. To by vás mělo přesměrovat na http://localhost:5000/unconfirmed.

Pokračujte a potvrďte e-mail a budete mít přístup ke všem stránkám. Bum!



Znovu odeslat e-mail

Nakonec zprovozněme odkaz pro opětovné odeslání. Přidejte následující funkci zobrazení do 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'))

Nyní aktualizujte unconfirmed.html šablona:

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


Třetí test

Víte, jak to chodí. Tentokrát nezapomeňte znovu odeslat nový potvrzovací e-mail a vyzkoušejte odkaz. Mělo by to fungovat.

Nakonec, co se stane, když si pošlete pár potvrzovacích odkazů? Jsou všechny platné? Vyzkoušejte to. Zaregistrujte nového uživatele a poté odešlete několik nových potvrzovacích e-mailů. Zkuste to potvrdit prvním e-mailem. Fungovalo to? Mělo by. je to v pořádku? Myslíte si, že platnost těchto ostatních e-mailů by měla vypršet, pokud bude odeslán nový?

Udělejte si o tom průzkum. A otestujte další webové aplikace, které používáte. Jak takové chování zvládají?



Aktualizujte testovací sadu

V pořádku. Takže to je vše pro hlavní funkce. Co kdybychom aktualizovali aktuální testovací sadu, protože je, no, nefunkční.

Spusťte testy:

$ python manage.py test

Měla by se zobrazit následující chyba:

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

Abychom to napravili, stačí aktualizovat setUp() metoda v 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()

Nyní spusťte testy znovu. Vše by mělo projít!



Závěr

Zjevně toho můžeme udělat mnohem víc:

  1. E-maily s bohatým a prostým textem – měli bychom rozesílat oba.
  2. E-mail pro resetování hesla – Měl by být zaslán uživatelům, kteří zapomněli svá hesla.
  3. Správa uživatelů – Měli bychom uživatelům umožnit aktualizovat jejich e-maily a hesla, a když se e-mail změní, měli bychom to znovu potvrdit.
  4. Testování – Potřebujeme napsat více testů, abychom pokryli nové funkce.

Stáhněte si celý zdrojový kód z úložiště Github. Komentář níže s dotazy. Podívejte se na část 2.

Veselé svátky!



  1. Příklady CURDATE() – MySQL

  2. Nahraďte duplicitní mezery jednou mezerou v T-SQL

  3. Jak zakázat všechna omezení cizího klíče v databázi SQL Server - SQL Server / TSQL výukový program, část 77

  4. Funkce Postgres Hodnota NULL pro řádek, který odkazuje na NEW