Home Tags Anciens articles Mon CV

Flask : login

This joke never gets old

Après avoir vu dans un précédent article les APIs REST, aujourd’hui je vais vous montrer les bases du formulaires de login en Flask et comment l’implémenter via le module : _flasklogin.

Il vous faut tout d’abord installer le module nécessaire, Flask étant toujours un microframework à l’heure qu’il est. Il n’embarque que le nécessaire.

Je vais présupposer que votre environnement est paramétré, que ce soit un venv ou non : utilisez pip pour installer la dernière version du module :

$ pip install flask_login # installation en soi
$ pip install flask_sqlalchemy flask # là, c'est pour faire marcher le bout de code que je vais vous montrer après

Une fois que cela est fait, démarrez un nouveau fichier .py. En premier lieu, on importe uniquement ce dont on va se servir plus tard, inutile de gaver la mémoire en chargeant des composants inutiles. Ces composants seront détaillés au fur et à mesure de leur utilisation dans le code d’exemple.

#!/usr/bin/env python
# coding: utf-8

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user

Initialisons notre app Flask, ainsi que quelques variables qui lui sont destinées, en l’occurrence sa _SECRETKEY et le chemin de sa base SQLite, afin de stocker les utilisateurs de notre app. Au passage, nous créons notre objet db qui nous servira pour interagir avec notre base de donnée.

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/login.db'
app.config['SECRET_KEY'] = 'Vr*?7Y@_eZF4Aw&'

db = SQLAlchemy(app)

Ceci étant fait, nous sommes plutôt bien partis pour coder notre app. Mais, il nous faut aussi initier le module qui fait l’objet de cet article, _flasklogin, afin de pouvoir l’exploiter. On utilise pour cela l’objet _LoginManager importé plus tôt.

login_manager = LoginManager()
login_manager.init_app(app)

OK, nous sommes sur les rails. Créons notre modèle de base de donnée via l’ORM _flasksqlalchemy. Il sera assez simple, nous n’avons besoin que d’une table Users avec des noms pour cet exemple. Libre à vous plus tard d’ajouter des champs comme… Par exemple… Le password (chiffré, bien entendu).

Notez que nous utilisons là UserMixin, qui vient de _flasklogin et non de _flasksqlalchemy. Notre classe User hérite en fait de cette classe UserMixin, à des fins de simplicité. En effet, cette dernière fournit simplement les implémentations par défaut de toutes les propriétés et méthodes pour ce genre de modèle.

class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(30), unique=True)

    def __init__(self, name):
        self.username = name 

Nous devons maintenant définir une fonction spécifique, _loaduser, qui sera agrémentée du décorateur login_manager.user_loader. Elle permet d’établir le callback appelé lors du reload d’un utilisateur depuis sa session. Son paramètre est la variable permettant de retrouver l’utilisateur en base, en l’occurence un id, défini dans notre classe User:

@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))

Nous allons implémenter une fonction pour nous faciliter la vie et peupler la base de donnée à notre place, ce qui nous évitera d’aller dans une console python et appeler nos méthodes à la main :

@app.route('/populate')
def populate_db():
    db.drop_all()
    db.create_all()
    me = 'Sandro'
    db.session.add(User(me))
        db.session.commit() # Merci Anthony ;)
    return('You\'re in the database, {0}'.format(me))

C’est plus pratique. Bon. Soufflez un peu, on continue.

Je considère que vous avez bu une mousse et que vous êtes chauds pour continuer.

Il nous faut implémenter l’authentification au sens propre, celle qui va nous servir à protéger nos autres pages. Pour cela, nous utilisons la méthode _loginuser, qui va… Nous logguer et établir une session dans le programme en cours.

Cette méthode va chercher notre utilisateur que nous sommes nous-même allé chercher via une belle requête digérée par notre ORM favori:

@app.route('/login')
def login():
    user = User.query.filter_by(username='Sandro').first()
    login_user(user)
    return 'Logged in'

En conséquence, une fois que nous avons visiter /login, nous sommes officiellement loggués ! Well done.

Mais comment protéger nos pages par la directive:

Si tu es pas loggué, tu peux pas entré

Un videur de boîte ?

Réponse : avec le décorateur _loginrequired (oui, encore un qu’on a importé de _flasklogin), et de la manière suivante. En l’occurrence une page /home qui nous dira « Bonjour » et l’utilisateur avec lequel on est loggué :

@app.route('/home')
@login_required
def home():
    return 'Current logged user is ' + current_user.username 

Utilisation surprise d’un élément : _currentuser, qui stocke les attributs de l’utilisateur en cours.

Si vous visitez cette page avant de visiter /login, vous allez écoper d’une belle erreur qui aura plus ou moins cette tête :

Vouuuuuuuuus ne passereeeez paaaaaaaas 🙂

Pour finir en beauté, voici comment vous délogguer de l’application et ainsi finir votre session. Nous allons utiliser le bien nommé _logoutuser, qui nous vient toujours du même module. Il est évident que pour accéder à la page de logout, il faut être loggué. Donc…

@app.route('/logout')
@login_required
def logout():
    logout_user()
    return 'Logged out'

Voilà, nous avons fait un premier tour de ce module et de ses fonctionnalités premières. Dans un prochain article, je développerais sans doute comment intégrer une template Jinja dans le processus afin d’avoir une jolie page de login, logout ou 401.

En attendant…

Roulez jeunesse !!