##### 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**. .. only:: html .. seealso:: Pourquoi les sessions ? Qu'est-ce que c'est ? A quoi cela sert-il ? .. only:: latex .. hint:: 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 ***** .. only:: html 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. .. only:: latex Pour implémenter les sessions à notre application, nous utiliserons le middleware ``express_session`` (dont vous trouverez plus d'informations à l'aide du lien suivant : http://j.mp/1RliRTy). 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 : .. code-block:: javascript // 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`` : .. captionup:: ./serveur/session.js .. code-block:: javascript 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 : .. captionup:: ./serveur/session.js .. code-block:: javascript 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" : .. captionup:: ./serveur/session.js .. code-block:: javascript 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 : .. captionup:: ./serveur/session.js .. code-block:: javascript 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 : .. captionup:: ./serveur/session.js .. code-block:: javascript 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 : .. captionup:: ./serveur/session.js .. code-block:: javascript 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 : .. captionup:: ./app.js .. code-block:: javascript 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); .. captionup:: ./serveur/routes.js .. code-block:: javascript exports.f = function( app, session, express, path, dossier, mysql, sql, express_session, crypto, base64url) { // ... } .. captionup:: ./serveur/socket.js .. code-block:: javascript 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 : .. captionup:: ./serveur/routes.js .. code-block:: javascript 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. .. liens .. _express_session: https://github.com/expressjs/session