Database
 sql >> Baza danych >  >> RDS >> Database

Obsługa potwierdzenia e-mail podczas rejestracji w Flask

W tym samouczku szczegółowo opisano, jak zweryfikować adresy e-mail podczas rejestracji użytkownika.

Zaktualizowano 30.04.2015 :Dodano obsługę Pythona 3.

W zakresie przepływu pracy, po zarejestrowaniu nowego konta przez użytkownika, wysyłany jest e-mail potwierdzający. Konto użytkownika jest oznaczone jako „niepotwierdzone”, dopóki użytkownik, no cóż, „potwierdzi” konto za pomocą instrukcji w e-mailu. Jest to prosty przepływ pracy, który stosuje większość aplikacji internetowych.

Jedną ważną rzeczą, którą należy wziąć pod uwagę, jest to, co mogą robić niepotwierdzeni użytkownicy. Innymi słowy, czy mają pełny dostęp do Twojej aplikacji, ograniczony/ograniczony dostęp, czy w ogóle nie mają dostępu? W przypadku aplikacji w tym samouczku niepotwierdzeni użytkownicy mogą się zalogować, ale są natychmiast przekierowywani na stronę przypominającą im, że muszą potwierdzić swoje konto, zanim będą mogli uzyskać dostęp do aplikacji.

Zanim zaczniemy, większość funkcjonalności, które będziemy dodawać, jest częścią rozszerzeń Flask-User i Flask-Security - co nasuwa pytanie, dlaczego po prostu nie korzystać z rozszerzeń? Cóż, przede wszystkim jest to okazja do nauki. Ponadto oba te rozszerzenia mają ograniczenia, takie jak obsługiwane bazy danych. Co by było, gdybyś chciał na przykład użyć RethinkDB?

Zacznijmy.


Podstawowa rejestracja kolby

Zaczniemy od schematu Flask, który obejmuje podstawową rejestrację użytkownika. Pobierz kod z repozytorium. Po utworzeniu i aktywowaniu środowiska wirtualnego uruchom następujące polecenia, aby szybko rozpocząć:

$ 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

Sprawdź plik readme, aby uzyskać więcej informacji.

Po uruchomieniu aplikacji przejdź do http://localhost:5000/register i zarejestruj nowego użytkownika. Zauważ, że po rejestracji aplikacja automatycznie loguje Cię i przekierowuje na stronę główną. Rozejrzyj się, a następnie przejrzyj kod – konkretnie schemat „użytkownika”.

Po zakończeniu zabij serwer.



Zaktualizuj bieżącą aplikację


Modele

Najpierw dodajmy confirmed pole do naszego User model w 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

Zwróć uwagę, że to pole ma domyślną wartość „Fałsz”. Dodaliśmy również confirmed_on pole, które jest [datetime ] (https://realpython.com/python-datetime/). Lubię też uwzględnić to pole, aby przeanalizować różnicę między registered_on i confirmed_on daty przy użyciu analizy kohortowej.

Zacznijmy całkowicie od nowa z naszą bazą danych i migracjami. Więc śmiało usuń bazę danych dev.sqlite , a także folder „migracje”.



Zarządzaj poleceniem

Następnie w obrębie manage.py , zaktualizuj create_admin polecenie, aby uwzględnić nowe pola bazy danych:

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

Pamiętaj, aby zaimportować datetime . Teraz kontynuuj i ponownie uruchom następujące polecenia:

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


register() funkcja przeglądania

Na koniec, zanim będziemy mogli ponownie zarejestrować użytkownika, musimy dokonać szybkiej zmiany w register() funkcja przeglądania w project/user/views.py

Zmień:

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

Do:

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

Ma sens? Zastanów się, dlaczego chcemy domyślnie confirmed na False .

Dobra. Uruchom aplikację ponownie. Przejdź do http://localhost:5000/register i ponownie zarejestruj nowego użytkownika. Jeśli otworzysz bazę danych SQLite w przeglądarce SQLite, powinieneś zobaczyć:

Tak więc nowy użytkownik, którego zarejestrowałem, [email protected] , nie jest potwierdzone. Zmieńmy to.




Dodaj potwierdzenie e-mail


Wygeneruj token potwierdzenia

Potwierdzenie e-mail powinno zawierać unikalny adres URL, który użytkownik po prostu musi kliknąć, aby potwierdzić swoje konto. Idealnie adres URL powinien wyglądać mniej więcej tak - http://yourapp.com/confirm/<id> . Kluczem jest tutaj id . Zamierzamy zakodować adres e-mail użytkownika (wraz ze znacznikiem czasu) w id używając swojego niebezpiecznego pakietu.

Utwórz plik o nazwie project/token.py i dodaj następujący kod:

# 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 więc w generate_confirmation_token() używamy funkcji URLSafeTimedSerializer wygenerowanie tokena z wykorzystaniem adresu e-mail otrzymanego podczas rejestracji użytkownika. rzeczywista e-mail jest zakodowany w tokenie. Następnie, aby potwierdzić token, w ramach confirm_token() możemy użyć funkcji loads() metoda, która jako argumenty przyjmuje token i wygaśnięcie - ważne przez godzinę (3600 sekund). Dopóki token nie wygaśnie, zwróci wiadomość e-mail.

Pamiętaj, aby dodać SECURITY_PASSWORD_SALT do konfiguracji Twojej aplikacji (BaseConfig() ):

SECURITY_PASSWORD_SALT = 'my_precious_two'


Zaktualizuj register() funkcja przeglądania

Teraz zaktualizujmy register() wyświetl ponownie funkcję z 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)

Pamiętaj też o zaktualizowaniu importów:

from project.token import generate_confirmation_token, confirm_token


Obsługuj potwierdzenie e-mail

Następnie dodajmy nowy widok do obsługi potwierdzenia e-mailem:

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

Dodaj to do project/user/views.py . Pamiętaj również o zaktualizowaniu importów:

import datetime

Tutaj nazywamy confirm_token() funkcja, przekazując w tokenie. Jeśli się powiedzie, aktualizujemy użytkownika, zmieniając email_confirmed atrybut na True i ustawienie datetime kiedy nastąpiło potwierdzenie. Ponadto w przypadku, gdy użytkownik przeszedł już proces potwierdzenia – i został potwierdzony – powiadamiamy o tym użytkownika.



Utwórz szablon wiadomości e-mail

Następnie dodajmy podstawowy szablon wiadomości e-mail:

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

Zapisz to jako activate.html w „projekt/szablony/użytkownik”. To zajmuje pojedynczą zmienną o nazwie confirm_url , który zostanie utworzony w register() funkcja przeglądania.



Wyślij e-mail

Stwórzmy podstawową funkcję do wysyłania e-maili z niewielką pomocą Flask-Mail, który jest już zainstalowany i skonfigurowany w project/__init__.py .

Utwórz plik o nazwie 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)

Zapisz to w folderze „projekt”.

Po prostu musimy przekazać listę odbiorców, temat i szablon. Za chwilę zajmiemy się ustawieniami konfiguracji poczty.



Zaktualizuj register() funkcja przeglądania w project/user/views.py (ponownie!)

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

Dodaj również następujący import:

from project.email import send_email

Tutaj składamy wszystko razem. Ta funkcja zasadniczo działa jako kontroler (bezpośrednio lub pośrednio) dla całego procesu:

  • Obsługa pierwszej rejestracji,
  • Wygeneruj token i adres URL potwierdzenia,
  • Wyślij e-mail z potwierdzeniem,
  • Potwierdzenie Flash,
  • Zaloguj się użytkownika i
  • Przekieruj użytkownika.

Czy zauważyłeś _external=True argument? Spowoduje to dodanie pełnego bezwzględnego adresu URL, który zawiera nazwę hosta i port (w naszym przypadku http://localhost:5000).

Zanim będziemy mogli to przetestować, musimy skonfigurować nasze ustawienia poczty.



Poczta

Zacznij od aktualizacji BaseConfig() w 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]'

Sprawdź oficjalną dokumentację Flask-Mail, aby uzyskać więcej informacji.

Jeśli masz już konto GMAIL, możesz z niego skorzystać lub zarejestrować testowe konto GMAIL. Następnie ustaw tymczasowo zmienne środowiskowe w bieżącej sesji powłoki:

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

Jeśli Twoje konto GMAIL ma uwierzytelnianie dwuetapowe, Google zablokuje próbę.

Teraz przetestujmy!




Pierwszy test

Uruchom aplikację i przejdź do http://localhost:5000/register. Następnie zarejestruj się za pomocą adresu e-mail, do którego masz dostęp. Jeśli wszystko poszło dobrze, powinieneś mieć e-mail w swojej skrzynce odbiorczej, który wygląda mniej więcej tak:

Kliknij adres URL i powinieneś przejść do http://localhost:5000/. Upewnij się, że użytkownik jest w bazie danych, pole „potwierdzone” to True i jest datetime powiązane z confirmed_on pole.

Fajnie!



Obsługuj uprawnienia

Jeśli pamiętasz, na początku tego samouczka zdecydowaliśmy, że „niepotwierdzeni użytkownicy mogą się logować, ale powinni zostać natychmiast przekierowani na stronę – nazwijmy trasę /unconfirmed - przypominanie użytkownikom, że muszą potwierdzić swoje konto, zanim będą mogli uzyskać dostęp do aplikacji”.

Więc musimy...

  1. Dodaj /unconfirmed trasa
  2. Dodaj niepotwierdzony.html szablon
  3. Zaktualizuj register() funkcja przeglądania
  4. Utwórz dekoratora
  5. Zaktualizuj navigation.html szablon

Dodaj /unconfirmed trasa

Dodaj następującą trasę 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')

Widziałeś już podobny kod, więc przejdźmy dalej.



Dodaj niepotwierdzony.html szablon

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

Zapisz to jako unconfirmed.html w „projekt/szablony/użytkownik”. Znowu wszystko powinno być proste. Na razie właśnie dodaliśmy fałszywy adres URL do ponownego wysłania wiadomości e-mail z potwierdzeniem. Zajmiemy się tym dalej.



Zaktualizuj register() funkcja przeglądania

Teraz po prostu zmień:

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

Do:

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

Tak więc, po wysłaniu e-maila z potwierdzeniem, użytkownik jest teraz przekierowywany do /unconfirmed trasa.



Utwórz dekoratora

# 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

Tutaj mamy podstawową funkcję sprawdzania, czy użytkownik nie jest potwierdzony. Jeśli nie zostanie potwierdzony, użytkownik zostanie przekierowany do /unconfirmed trasa. Zapisz to jako decorators.py w katalogu „projekt”.

Teraz udekoruj profile() funkcja przeglądania:

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

Pamiętaj, aby zaimportować dekorator:

from project.decorators import check_confirmed


Zaktualizuj navigation.html szablon

Na koniec zaktualizuj następującą część navigation.html szablon-

Zmień:

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

Do:

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

Czas ponownie przetestować!




Drugi test

Uruchom aplikację i zarejestruj się ponownie, używając adresu e-mail, do którego masz dostęp. (Możesz usunąć z bazy danych starego użytkownika, którego zarejestrowałeś wcześniej, aby użyć go ponownie.) Teraz po rejestracji powinieneś zostać przekierowany na stronę http://localhost:5000/unconfirmed.

Przetestuj trasę http://localhost:5000/profile. Powinno to przekierować Cię do http://localhost:5000/unconfirmed.

Śmiało i potwierdź wiadomość e-mail, a będziesz mieć dostęp do wszystkich stron. Bum!



Wyślij ponownie e-mail

Na koniec sprawmy, aby link do ponownego wysłania działał. Dodaj następującą funkcję widoku 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'))

Teraz zaktualizuj unconfirmed.html szablon:

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


Trzeci test

Znasz zasady. Tym razem wyślij ponownie nowy e-mail z potwierdzeniem i przetestuj link. Powinno działać.

Na koniec, co się stanie, jeśli wyślesz sobie kilka linków potwierdzających? Czy każdy jest ważny? Przetestuj to. Zarejestruj nowego użytkownika, a następnie wyślij kilka nowych e-maili potwierdzających. Spróbuj potwierdzić w pierwszym e-mailu. Zadziałało? Powinno. Czy to w porządku? Czy uważasz, że te inne e-maile powinny wygasnąć, jeśli zostanie wysłany nowy?

Zrób trochę badań na ten temat. Przetestuj inne aplikacje internetowe, z których korzystasz. Jak radzą sobie z takim zachowaniem?



Zaktualizuj pakiet testowy

W porządku. A więc to tyle, jeśli chodzi o główną funkcjonalność. Co powiesz na zaktualizowanie obecnego zestawu testowego, ponieważ jest, no cóż, zepsuty.

Uruchom testy:

$ python manage.py test

Powinieneś zobaczyć następujący błąd:

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

Aby to naprawić, wystarczy zaktualizować setUp() metoda w 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()

Teraz ponownie uruchom testy. Wszystko powinno minąć!



Wniosek

Możemy zrobić znacznie więcej:

  1. E-maile sformatowane w porównaniu ze zwykłym tekstem – powinniśmy wysyłać oba.
  2. E-mail dotyczący resetowania hasła — powinien zostać wysłany do użytkowników, którzy zapomnieli hasła.
  3. Zarządzanie użytkownikami — Powinniśmy umożliwić użytkownikom aktualizowanie ich adresów e-mail i haseł, a po zmianie adresu e-mail należy to ponownie potwierdzić.
  4. Testowanie — musimy napisać więcej testów, aby uwzględnić nowe funkcje.

Pobierz cały kod źródłowy z repozytorium Github. Komentarz poniżej z pytaniami. Sprawdź część 2.

Wesołych świąt!



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Treść nieustrukturyzowana:niewykorzystane źródło paliwa dla sztucznej inteligencji i uczenia maszynowego

  2. Zrozumienie 3 kluczowych cech Big Data

  3. Niektóre DOWOLNE przekształcenia zbiorcze są uszkodzone

  4. KLUCZ OBCY SQL

  5. 5 sposobów na wyświetlenie tymczasowych tabel za pomocą T-SQL