##### TP - connexion et déconnexion ##### Nous allons à présent nous attaquer à une fonctionnalité indispensable pour tout forum : la connexion des utilisateurs. A la fin de ce travail pratique, la page suivante s'affichera : .. image:: tp2/1.png En rétrécissant la fenêtre, nous obtenons ce résultat : .. image:: tp2/2.png :height: 10000 px :width: 700 px :scale: 55 % Cette page est accessible à partir d'un bouton dans la barre de navigation. A côté du bouton ``login`` de la barre de navigation, le bouton ``Créer un compte`` redirige vers la page ``nouveau_compte`` (qui n'existe pas encore). .. image:: tp2/5.png :height: 10000 px :width: 150 px :scale: 60 % Une alerte bleue invite l'utilisateur à créer un compte s'il n'en a pas encore. .. image:: tp2/6.png :height: 10000 px :width: 750 px :scale: 60 % Un clic sur le bouton ``login`` ou un appui sur la touche ``enter`` depuis la zone de texte ``mot de passe`` vérifie si le nom d'utilisateur existe et si le mot de passe est correct. Si ces conditions sont vérifiées, l'utilisateur est alors connecté. Dans le cas contraire, un message s'affiche : .. image:: tp2/3.png :height: 10000 px :width: 700 px :scale: 55 % Lorsque l'utilisateur est connecté, sa session est sauvegardée dans le cache de son navigateur, il est redirigé vers la page d'accueil et son nom d'utilisateur s'affiche dans la barre de navigation. Les boutons ``Créer un compte`` et ``Login`` disparaissent au profit du bouton ``Logout`` qui permet à l'utilisateur de se déconnecter. .. image:: tp2/4.png :height: 10000 px :width: 150 px :scale: 60 % Si l'utilisateur clique sur le bouton ``Logout``, sa session (celle qui est stockée dans le navigateur internet) est supprimée et il est redirigé vers la page de login. Pistes ***** - Comme la page pour créer un compte n'existe pas encore, vous pouvez tester votre page de connexion avec l'utilisateur ``samf``, déjà présent dans votre base de données, qui a le mot de passe ``hello`` - Pour écrire les instructions nécessaires à la mise en place de la session, utilisez la procédure vue au chapitre précédent Bon travail ! Correction ***** Code côté serveur ===== Pour que cette page s'affiche, il faut commencer par créer un fichier ``EJS`` et l'indiquer dans ``routes.js``. Ici, le nom de ``login.ejs`` a été choisi pour ce fichier, qui se trouve dans le dossier ``views``. Le fichier ``routes.js`` a alors besoin d'un nouveau ``app.get()`` pour afficher cette page. .. captionup:: ./serveur/routes.js .. code-block:: javascript app.get('/login', function(req, res) { res.render('login.ejs'); }); Il s'agit ensuite de vérifier, avec ``socket.js``, si le nom d'utilisateur existe et correspond avec le mot de passe : .. captionup:: ./serveur/socket.js .. code-block:: javascript socket.on('login', function (login) { var requete_sql = '\ SELECT mot_de_passe \ FROM users \ WHERE nom_utilisateur = ??'; var inserts = [login.nom_utilisateur]; requete_sql = sql.preparer(mysql, requete_sql, inserts); sql.requete(mysql, sql, requete_sql, function(results) { try { /* ce bloc try-catch sert à détecter une erreur pouvant survenir lorsqu'on demande le mot de passe d'un utilisateur inexistant */ if (login.password == results[0].mot_de_passe) { session.creation_jeton(mysql, sql, base64url, crypto, socket, login.nom_utilisateur); } else { socket.emit('erreur_login'); } } catch(e) { socket.emit('erreur_login'); } }); }); Lors de l'appel de cette fonction, une requête ``SQL`` est exécutée pour savoir si le mot de passe correspond à l'utilisateur. Si tout est correct, la fonction ``creation_jeton`` est exécutée, et cette fonction redirige l'utilisateur vers la page ``token/:jeton``. Dans le cas contraire, ``socket.io`` émet l'événement ``erreur_login`` qui, sur le côté client, fera apparaître un message d'erreur. Nous vérifions alors ce jeton d'authentification et enregistrons la session. Pour ce faire, nous ajoutons un ``app.get`` qui s'exécutera lorsque la page ``token/:token`` sera demandée : .. captionup:: ./serveur/routes.js .. code-block:: javascript app.get('/token/:token', function(req, res) { session.conv_jeton(mysql, sql, req.params.token, function(nom_utilisateur) { session.login(res, req.session, nom_utilisateur, function() { // le chiffre 301 indique au navigateur web que la page va être redirigée res.redirect(301, '/'); }); }); }); Il manque encore la gestion de la page ``Logout`` dans ``routes.js`` : .. captionup:: ./serveur/routes.js .. code-block:: javascript app.get('/logout', function(req, res) { session.logout(req.session); res.redirect(301, '/login'); }); Après la suppression de la session, l'utilisateur est redirigé vers la page de login. Code côté client ===== Commençons par le code ``EJS`` de la page principale, ``login.ejs`` : .. captionup:: ./views/login.ejs .. code-block:: guess <% include balise_head %> <% include barre_navigation %>
Vous n'avez pas encore de compte ? Inscrivez-vous maintenant !

Se connecter

Le nom d'utilisateur ou le mot de passe est incorrect.
Login
Pour fonctionner correctement, cette page a besoin des fonctions ``login()``, ``verifier_enter()`` et de la gestion de plusieurs événements ``socket``. Nous allons donc créer un nouveau fichier ``javascript``, ``login.js`` : .. captionup:: ./static/js/login.js .. code-block:: javascript // cette fonction envoie l'événement socket "login" function login() { socket.emit('login', { nom_utilisateur : byId('login_utilisateur').value, password : byId('login_password').value }); } function verifier_enter(event, form, callback) { // fonction tirée de http://stackoverflow.com/questions/14251676/ var code = (event.keyCode ? event.keyCode : event.which); if(code === 13) { callback(); } } // met les zones de texte en rouge et affiche le message d'erreur socket.on('erreur_login', function() { byId('form_login_utilisateur').className += ' has-error'; byId('form_login_password').className += ' has-error'; byId('login_incorrect').style.display = 'inline'; }); // redirige l'utilisateur vers l'URL indiquée dans l'événement socket // (en l'occurence, la page "token") socket.on('redirection', function(url) { window.location.assign(url); }); Maintenant, la page ``Login`` vérifie si le nom d'utilisateur est correct et redirige l'utilisateur vers la page d'accueil tout enregistrant le nom d'utilisateur dans la session du navigateur internet. Barre de navigation ----- Nous paramétrons la barre de navigation afin qu'elle affiche le nom d'utilisateur et le bouton ``Logout`` si l'utilisateur est connecté, et les boutons ``Créer un compte`` et ``Login`` dans le cas contraire. Pour parvenir à ce résultat, nous pouvons utiliser des conditions directement dans ``EJS``, qui nous permettront de modifier directement les éléments de la barre de navigation : .. captionup:: ./views/barre_navigation.ejs .. code-block:: guess Pour terminer, nous incluons la variable ``nom_utilisateur`` dans le ``res.render()`` de toutes les pages pour qu'``EJS`` ne retourne pas une erreur. Pour cela, nous utilisons la fonction ``session.session_active()``. Notre fichier ``routes.js`` devrait alors ressembler à ceci : .. captionup:: ./serveur/routes.js .. code-block:: javascript exports.f = function( app, session, express, path, dossier, mysql, sql, express_session, crypto, base64url) { var session_secret = base64url(crypto.randomBytes(20)).replace('-', '7') session.session_init(app, express_session, session_secret); // la session est initiée dans express.js app.get('/token/:token', function(req, res) { session.conv_jeton(mysql, sql, req.params.token, function(nom_utilisateur) { session.login(res, req.session, nom_utilisateur, function() { res.redirect(301, '/'); }); }); }); app.get('/login', function(req, res) { res.render('login.ejs', { nom_utilisateur: session.session_active(req.session, req) }); }); app.get('/logout', function(req, res) { session.logout(req.session); res.redirect(301, '/login'); }); app.get(/^\/home|\/$/, function(req, res) { sql.chargement_discussions(mysql, sql, true, function(discussions) { res.render('index.ejs', { nom_utilisateur: session.session_active(req.session, req), discussions: discussions }); }); }); app.get('/derniere_discussion', function(req, res) { sql.chargement_discussions(mysql, sql, false, function(discussion) { res.render('chargement_discussions.ejs', { discussions: discussion }); }); }); app.get(/static\/([0-9a-z\.\/_-]+)$/i, function(req, res) { res.sendFile(dossier + '/static/' + req.params[0]); }); app.use(function(req, res, next) { res.render('error.ejs'); }); }; Et voilà ! ``samf`` apparaît dans la barre de navigation lorsqu'on s'est connecté ! .. liens: .. _`cette rubrique`: sessions.html#fonctionnement-general