pip install flask-login
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 :
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 :
@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 :
def verify_password(self, password): return check_password_hash(self.password_hash, password)
Initialiser le LoginManager lors de la création de l'application :
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.
# ... 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))
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')
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)
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'))
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 %}
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.'