====== 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** : 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) ===== Initialisation ===== 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)) ===== Formulaire de login ===== 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 ===== 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, %s !" % 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 %} Log Out {% else %} Log In {% 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.'