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 :

_images/17.png

En rétrécissant la fenêtre, nous obtenons ce résultat :

_images/25.png

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).

_images/51.png

Une alerte bleue invite l’utilisateur à créer un compte s’il n’en a pas encore.

_images/61.png

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 :

_images/35.png

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.

_images/42.png

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.

./serveur/routes.js
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 :

./serveur/socket.js
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 :

./serveur/routes.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() {
      // 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 :

./serveur/routes.js
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 :

./views/login.ejs
<!DOCTYPE html>
<html lang="fr">
  <head>
    <% include balise_head %>
    <script src="static/js/login.js"></script>
  </head>

  <body>
    <% include barre_navigation %>

    <div class="container">
      <div class="row">
        <div class="col-lg-offset-2 col-md-offset-1 col-md-10 col-lg-8
        blanc corps_page">
          <center>
            <div class="alert alert-info">
                Vous n'avez pas encore de compte ?
                <a href="nouveau_compte">Inscrivez-vous</a> maintenant !
            </div>
          </center>
          <form class="form-horizontal" style="padding-right:20px;">
            <div class="form-group">
              <h4 class="gros_titre">Se connecter</h4>
            </div>
            <div class="row">
              <div class="form-group" id="form_login_utilisateur">
                <label for="login_utilisateur" class="col-lg-3 col-sm-4 control-label">
                  Nom d'utilisateur
                </label>
                <div class="col-lg-9 col-sm-8">
                  <input type="text" class="form-control" id="login_utilisateur">
                  <span class="help-block message_erreur"
                    id="login_incorrect"
                    style="position: relative; top: 6px;">
                      Le nom d'utilisateur ou le mot de passe est incorrect.
                  </span>
                </div>
              </div>
            </div>
            <div class="row">
              <div class="form-group" id="form_login_password">
                <label for="login_password" class="col-lg-3 col-sm-4 control-label">
                  Mot de passe
                </label>
                <div class="col-lg-9 col-sm-8">
                  <input type="password"
                    class="form-control"
                    id="login_password"
                    onkeypress="verifier_enter(event, this, login);">
                </div>
              </div>
            </div>
            <div class="form-group">
              <span
                class="pull-right btn btn-primary"
                style="margin-top: -8px; margin-bottom: 16px;"
                onclick="login();">
                  Login
              </span>
            </div>
          </form>
        <div class="col-lg-offset-2 col-md-offset-1"></div>
      </div>
    </div>
  </body>
</html>

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 :

./static/js/login.js
// 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 :

./views/barre_navigation.ejs
<nav class="navbar navbar-default" id="navbar" style="height:50px;">
  <div class="navbar-header">
    <a class="navbar-brand" href="/">Forum</a>
      <a href="#"
        class="navbar-toggle"
        onclick="navbar_mobile();">
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
      </a>
  </div>
  <div class="navbar-collapse collapse" id="contenu_nav">
    <ul class="nav navbar-nav">
      <li><a href="/">Accueil</a></li>
    </ul>
    <ul class="nav navbar-nav navbar-right" style="padding-right:15px;">
      <li>
        <% if (nom_utilisateur !== 'session inexistante') { %>
          <li>
            <a href="profil_<%= nom_utilisateur %>" style="color:black;">
              <%= nom_utilisateur %>
            </a>
          </li>
        <% } %>
        <% if (nom_utilisateur === 'session inexistante') { %>
          <a href="nouveau_compte">
            Créer un compte
          </a>
        <% } %>
      <li>
        <% if (nom_utilisateur === 'session inexistante') { %>
          <a href="login">
            Login
          </a>
        <% } else { %>
          <a href="logout">
            Logout
          </a>
        <% } %>
      </li>
    </ul>
  </div>
</nav>

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 :

./serveur/routes.js
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é !