Sessions¶
Introduction¶
Avant de continuer la réalisation du forum, il est nécessaire de pouvoir maîtriser la gestion et le fonctionnement des sessions.
Question
Pourquoi les sessions ? Qu’est-ce que c’est ? A quoi cela sert-il ?
Les sessions sont des données (des chaînes de caractères ou des nombres) qui peuvent être stockées dans l’ordinateur (ou dans le smartphone) de l’utilisateur de l’application. Elles permettent donc de conserver des informations de manière locale. Et, vous vous en doutez, elles sont extrêmement utiles (voir essentielles) pour garder un utilisateur connecté à son compte dans n’importe quelle application web.
Prenons un exemple concret : lorsque vous vous connectez sur Google ou sur Facebook, vous devez entrer votre nom d’utilisateur et votre mot de passe. Ensuite, si vous fermez la fenêtre de votre navigateur ou que vous changez de page web, puis revenez consulter vos actualités sur Facebook ou Google, vous n’avez, en principe, pas besoin de vous reconnecter, car votre navigateur internet a gardé en mémoire votre nom d’utilisateur.
Fonctionnement général¶
Pour implémenter les sessions à notre application, nous utiliserons le middleware express_session. Nous aurons également besoin de crypto
et de base64url
, deux modules qui nous permettront de créer une clé secrète de session et un jeton d’authentification pour que l’utilisateur puisse se connecter.
Mais avant tout, étudions comment cela fonctionne dans le cas de notre application.
express_session
est un middleware expressjs
qui peut enregistrer de nouvelles sessions dans le cache du navigateur. Mais ce module pose une limite dans notre application. En effet, pour modifier la session de l’utilisateur, express_session
a besoin de l’objet session
de la variable req
(request). Or, l’accès à cette variable n’est pas possible depuis socket.io
. Nous ne pouvons enregistrer une session que depuis un app.get()
, donc lorsque l’utilisateur charge une page web ; mais il est impossible de le faire avec l’envoi d’un simple socket.emit()
Note
Pour corriger ce problème, il existe également un middleware expressjs
du nom de session.socket-io
permettant d’accéder à la session depuis la fonction qu’utilise socket.io
. Cependant, ce middleware est plus complexe à mettre en place et à comprendre. C’est pourquoi nous utiliserons express_session
dans cette application.
Par conséquent, pour créer une session depuis un socket.on()
, nous devrons procéder de la manière suivante :
// le serveur reçoit une requête socket.io
socket.on('login', function(login) {
// si le login est correct...
var token = //...
var requete_sql = 'INSERT INTO session(nom_utilisateur, token) VALUES("??", "??")'
// insertion du nom d'utilisateur et du token qui vient d'être créé
// exécution de la requête SQL
/* redirection vers une page pour pouvoir utiliser client_session
à partir d'un app.get() */
socket.emit('redirection', '/token/'+token);
});
// l'utilisateur est redirigé vers la page...
// le serveur reçoit une requête de page web
app.get('/token/:token', function(req, res) {
var requete_sql = 'SELECT nom_utilisateur FROM session WHERE token = ??';
var inserts = [req.params.token]
// exécution de la requête SQL
/* enregistrement du nom d'utilisateur récupéré à partir de la base de données
dans une nouvelle session */
// redirection de l'utilisateur vers la page d'accueil
});
Important
Bien entendu, les deux fonctions ci-dessus ne se trouvent pas dans le même fichier, et il manque des lignes de code (notamment celles pour enregistrer les sessions et exécuter les requêtes SQL
), mais avec ce condensé, vous devriez pouvoir mieux appréhender le système utilisé pour créer des sessions.
Code¶
Pour améliorer la structure de notre code, nous allons créer un nouveau fichier du nom de session.js
dans le dossier serveur
. Dans ce fichier, nous ajouterons quelques fonctions :
session_init()
est lancée en même temps que le serveur (lorsque vous écrivez sudo nodejs app.js
dans votre console nodejs
). Elle sert à initier la session avec une clé secrète générée aléatoirement avec les modules crypto
et base64url
:
exports.session_init = function(app, express_session, session_secret) {
app.use(express_session({
secret: session_secret,
name: 'utilisateur',
resave: true,
saveUninitialized: true
}));
};
login()
permet d’assigner le nom d’utilisateur à la session :
exports.login = function(res, sess, nom_utilisateur, callback) {
sess.nom_utilisateur = nom_utilisateur;
callback();
};
logout()
sert à supprimer une session. Cette fonction s’exécute lorsque l’utilisateur clique sur le bouton “Logout” :
exports.logout = function(sess) {
sess.destroy(function(err) {
});
};
session_active()
retourne, comme son nom l’indique, le nom d’utilisateur qui est enregistré dans la session :
exports.session_active = function(sess, req) {
if (sess.nom_utilisateur) {
return sess.nom_utilisateur;
} else {
return 'session inexistante';
}
};
creation_jeton()
permet d’assigner un nouveau jeton d’authentification (ou “token”) à un utilisateur en l’enregistrant dans la base de données. Cette fonction redirige ensuite l’utilisateur vers la page /token/:jeton_généré
, comme nous l’avons vu précédemment :
exports.creation_jeton = function(
mysql, sql, base64url,
crypto, socket, nom_utilisateur) {
var token = base64url(crypto.randomBytes(20)).replace('-', '_');
var requete_sql = '\
INSERT INTO session(nom_utilisateur, token) VALUES(??, "??")';
var inserts = [nom_utilisateur, token];
requete_sql = sql.preparer(mysql, requete_sql, inserts);
sql.requete(mysql, sql, requete_sql, function() {
socket.emit('redirection', '/token/'+token);
});
}
conv_jeton()
récupère le jeton contenu dans l’URL sur lequel a été redirigé l’utilisateur et va chercher dans la base de données à quel utilisateur il correspond pour ensuite enregistrer la session de cet utilisateur. Puis, il supprime le token de la base de données :
exports.conv_jeton = function(mysql, sql, token, callback) {
// même si la requête SQL est préparée, le token doit ici être mis en guillemets
// sinon, un problème survient lors de l'utilisation de la fonction mysql.format()
var requete_sql = 'SELECT nom_utilisateur FROM session WHERE token = "??"';
var inserts = [token];
requete_sql = sql.preparer(mysql, requete_sql, inserts);
sql.requete(mysql, sql, requete_sql, function(results) {
var requete_sql = '\
DELETE FROM session WHERE nom_utilisateur = '+results[0].nom_utilisateur;
sql.requete(mysql, sql, requete_sql);
callback(results[0].nom_utilisateur);
});
}
Il convient maintenant d’injecter les dépendances crypto
, base64url
, express_session
et ./serveur/session.js
dans les fonctions socket.f()
et routes.f()
pour que les fonctions que nous venons de créer puissent être utilisées :
var http = require('http');
var path = require('path');
var crypto = require('crypto');
var base64url = require('base64url');
var mysql = require('mysql');
var express = require('express');
var app = express();
var server = http.Server(app);
var io = require('socket.io')(server);
var express_session = require('express-session');
var routes = require('./serveur/routes.js');
var session = require('./serveur/session.js');
var socket = require('./serveur/socket.js');
var sql = require('./serveur/sql.js');
// injection des dépendances dans routes.js
routes.f(app, session, express,
path, __dirname, mysql,
sql, express_session, crypto, base64url);
// injection des dépendances dans socket.js
socket.f(io, mysql, sql, session, crypto, base64url);
server.listen(8888);
exports.f = function(
app, session, express, path,
dossier, mysql, sql,
express_session, crypto, base64url) {
// ...
}
exports.f = function(
io, mysql, sql,
session, crypto, base64url) {
// ...
}
Il ne reste alors plus qu’à lancer la fonction session_init
au démarrage du serveur :
exports.f(
// ...
){
// le chiffre 7 pourrait être n'importe quel autre chiffre ou lettre
var session_secret = base64url(crypto.randomBytes(20)).replace('-', '7')
session.session_init(app, express_session, session_secret);
}
Avec les sessions, nous avons maintenant à notre disposition une série de fonctions nous permettant de mettre en place la connexion et la déconnexion des utilisateurs.