Outils pour utilisateurs

Outils du site


Panneau latéral

Plan du Site:

python:flask:flask_login

Flask-Login

Installation

pip install flask-login

Classe User

La classe User de l'application doit implémenter quelques propriétés et méthodes, pour cela le plus simple est de la faire hériter de UserMixin :

app/models/user.py
from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash
 
class User(UserMixin, db.Model):
    __tablename__ = 'users'
    id            = db.Column(db.Integer, primary_key = True)
    email         = db.Column(db.String(64), unique=True, index=True)
    username      = db.Column(db.String(64), unique=True, index=True)
    password_hash = db.Column(db.String(128))
    role_id       = db.Column(db.Integer, db.ForeignKey('roles.id'))
    # ...

La classe UserMixin inclut les propriétés et méthodes nécessaires :

  • is_authenticated
  • is_active
  • is_anonymous
  • get_id()

Implémenter la propriété password qui sera en écriture seule, en effet on peut seulement écrire un nouveau password mais jamais le relire. L'écriture du password assigne le hash de la valeur donnée au champ password_hash :

app/models/user.py
    @property
    def password(self):
        raise AttributeError('password is not a readable attribute')
 
    @password.setter
    def password(self, password):
        self.password_hash = generate_password_hash(password)

Implémenter la méthode verify_password :

app/models/user.py
    def verify_password(self, password):
        return check_password_hash(self.password_hash, password)

Initialisation

Initialiser le LoginManager lors de la création de l'application :

app/__init__.py
from flask       import Flask
from flask_login import LoginManager
 
app = Flask(__name__)
 
# ...
 
login_manager = LoginManager()
login_manager.login_view = 'auth.login'
login_manager.init_app(app)
 
# ...

Dans la propriété login_view, on indique la route vers la page de connexion, vers laquelle sera redirigé un utilisateur tentant d'accéder à une page protégée.

Enfin, il faut définir une fonction qui sera appelée lorsque Flask-Login aura besoin de charger un utilisateur sur base de son identifiant.

app/__init__.py
# ...
 
from app.models.user import User
 
# ...
 
@login_manager.user_loader
def load_user(user_id):
 
    # Lecture de l'utilisateur depuis la base de données
    # L'identifiant est passé sous forme de chaîne, on le
    # transforme en int.
 
    return User.query.get(int(user_id))

Formulaire de login

app/forms/auth.py
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField
from wtforms.validators import DataRequired, Length, Email
 
class LoginForm(FlaskForm):
    email       = StringField('Email',
                  validators=[DataRequired(), Length(1, 64), Email()]
    password    = PasswordField('Password',
                  validators=[DataRequired()])
    remember_me = BooleanField('Keep le logged in')
    submit      = SubmitField('Log In')

Login

app/controllers/auth.py
from flask           import Blueprint
from flask           import request
from flask           import render_template, redirect, url_for, flash
from flask_login     import login_user
from app.forms.auth  import LoginForm
from app.models.user import User
 
auth = Blueprint('auth', __name__)
 
@auth.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm()
 
    # Si le formulaire a été posté (submit) et que son contenu
    # a passé toutes les validations
    if form.validate_on_submit():
 
        # Si l'utilisateur a cliqué sur "Login"
        if form.submit.data:
 
            # Rechercher l'utilisateur selon l'email entré
            user = User.query.filter_by(email = form.email.data).first()
 
            # Si l'utilisateur est trouvé, et que son mot de passe correspond
            if user is not None and user.verify_password(form.password.data):
 
                # Prendre en compte la connexion de l'utilisateur.
                # Si 'remember_me' vaut True, un cookie sera ajouté afin de
                # conserver la connexion active.
                login_user(user, form.remember_me.data)
                flash(u"Welcome, <b>%s</b> !" % user.name, "success")
 
                # Retourner une redirection
                # - soit vers la page 'suivante' si le login provient d'une
                #   tentative d'accès à une page protégée
                # - soit vers la page d'accueil
                next = request.args.get('next')
                if next is None or not next.startswith('/'):
                    next = url_for('main.home')
                return redirect(next)
 
            # Si l'utilisateur n'est pas trouvé, on ajoute un message flash
            # avant de retourner au formulaire
            flash(u"Invalid username or password.", "error")
 
    # Rendu du formulaire dans tous les cas non traités ci-dessus
    return render_template('auth/login.html', form = form)

Logout

from flask_login import logout_user, login_required
 
@auth.route('/logout')
@login_required
def logout():
    logout_user()
    flash(u"You have been logged out.", "success")
    return redirect(url_for('main.home'))

Objet current_user

La variable current_user est définie par Flask-Login, et elle disponible dans les templates et dans les vues.

Lorsqu'aucun utilisateur n'est connecté, current_user contient un objet dont la propriété is_authenticated vaut False

Lorsqu'un utilisateur est connecté, current_user contient un objet de la classe User.

from flask_login import current_user
 
# ...
 
if current_user.is_authenticated:
    print("Hello %s !" % current_user.name)

On peut afficher le lien Log In ou Log Out en fonction de l'état de l'utilisateur (connecté ou non):

{% if current_user.is_authenticated %}
  <a href="{{ url_for('auth.logout') }}">Log Out</a>
{% else %}
  <a href="{{ url_for('auth.login') }}">Log In</a>
{% endif %}

Protection d'une route

Avec le décorateur @login_required

:!: Il est important de mettre les deux décorateurs dans cet ordre.

from flask_login import login_required
 
@app.route('/secret')
@login_required
def secret():
    return 'Seuls les utilisateurs authentifiés verront ceci.'
python/flask/flask_login.txt · Dernière modification: 2022/10/30 04:50 par marclebrun