Passer au contenu principal

Si vous avez déjà développé des applications web, vous avez certainement eu affaire aux sessions.

Celles-ci servent en effet à contourner une limitation du protocole HTTP, à savoir que celui-ci est complètement stateless. Par l’intermédiaire de l’utilisation des cookies, qui sont envoyés dans le header de vos requêtes HTTP par votre navigateur le serveur web parvient à retrouver dans sa mémoire qui est l’utilisateur avec lequel il dialogue. Par ailleurs, il peut aussi conserver des données sur cet utilisateur appelés attributs de session.

Dans une application JEE, l’objet session HTTP implémente l’interface HttpSession.

Les attributs de session

Comme nous venons de le voir, il est possible de stocker des données de l’utilisateur en session, qu’on appelle les attributs de session. Ceux-ci peuvent-être de n’importe quel type. La session a en interne une Map qui permet de stocker ce qu’on veut.

Un point très important cependant : tous vos attributs de session doivent impérativement implémenter l’interface java.io.Serializable. En effet, des serveurs web dont Tomcat écrivent parfois les sessions utilisateur sur disque, notamment lorsqu’ils commencent à arriver à court de mémoire vive ou s’ils ont utilisés en load balancing. Si vous ne le faites pas, la sérialisation de la session va échouer, et vous perdrez des objets en mémoire. Dans la pratique, l’utilisateur sera contraint de se déconnecter de votre application pour se reconnecter, afin d’avoir une session « propre ».

Les attributs à mettre en session, et ceux qu’il ne faut y pas mettre

De manière générale, il convient de ne pas abuser des attributs de session. En effet, de par le fonctionnement des garbage collectors en Java, ceux-ci ont fortement tendance à finir dans la zone mémoire OldGen, et le seul moyen de les en déloger est de faire un full GC. Ce dernier peut durer plusieurs dizaines de secondes, même si vous utilisez le Concurrent Mark and Sweep (CMS) qui passe en mode « Stop-the-world » s’il ne lui reste pas assez d’espace mémoire pour opérer. Ce point peut être particulièrement gênant si vous avez des SLA.

De manière générale, les seuls éléments qui peuvent être mis en cache en session sont ceux privés pour un utilisateur donné ou ceux dont la modification n’a aucun impact en terme d’usage pour les autres. Je m’explique : prenons le cas d’un élément partagé entre deux utilisateurs A et B. Imaginons que A et B chargent, en même temps cet élément partagé. A le modifie et enregistre sa mise à jour. Le seul moyen pour que B voie l’objet mis à jour, sera de se déconnecter puis de se reconnecter. Pas vraiment pratique ! Donc pour les éléments partagés entre plusieurs utilisateurs, privilégiez un cache distribué de type EhCache.

Quelques bonnes pratiques avec les sessions

Pour éviter de contaminer votre code avec l’interface HttpSession, une bonne pratique consiste à créer un objet custom qui aura ce rôle. Ca augmentera la réutilisabilité de votre code, en particulier si vous devez l’utiliser en dehors d’un contexte web. Cet objet session se comportera de la même manière qu’une HttpSession, et veillera à être thread-safe pour éviter les effets de bord documentés ici

Pour éviter de vous balader avec cet objet en paramètre de votre API, partout, voici une solution très simple : créez un objet de type singleton ou service Spring. Dedans, mettez une variable en ThreadLocal qui contient votre objet de session. Pour l’alimenter, il vous suffit tout simplement d’utiliser un filtre HTTP (ou Spring MVC) qui va récupérer depuis la vraie HttpSession l’objet en question, et le mettre dans votre service. La seule limitation à ce système est que si à un moment donné votre application lance un Thread qui a besoin de l’objet session, il faut penser à réalimenter le service.

Et pour les services REST ou les services Web ?

De plus en plus les applications internet utilisent une API REST ou Soap. D’ailleurs c’est généralement une bonne idée de n’avoir qu’une seule API appelée par tout le monde car ça fait moins de choses à maintenir, et ces APIs se prêtent particulièrement bien à des tests de charge. Néanmoins, à l’instar d’HTTP, ces APIs sont stateless, ce qui peut poser des problèmes pour des services requérant une authentification (on va éviter de transmettre les login/password à chaque requête…). Pour garder ces infos et quelques autres, on peut adopter le schéma suivant : on propose un service d’authentification qui va donner un token au client quand tout se passe bien (avec une validité limitée et tout et tout). A chaque requête, le client renverra le token dans un header HTTP, qui sera analysé par un filtre qui redirigera l’utilisateur vers une page de login (ou un message d’erreur…) si ce token n’est plus valable. Pour éviter les attaques de type Man-in-the-middle, on fera dialoguer tout ce beau monde en HTTPS, qui devrait aussi d’ailleurs être utilisé pour les applis qui utilisent des cookies de session, précisément pour la même raison, mais on abordera ces problématiques de sécurité dans d’autres billets. On pourra pousser le concept jusqu’à sérialiser dans un blob en base ou ailleurs les données de notre « session », tout en limitant au maximum les données écrites dedans pour éviter de corrompre vos données en cas d’accès concurrent.

Cet article vous a plu ? Vous aimerez sûrement aussi :

Julien
Moi c’est Julien, ingénieur en informatique avec quelques années d’expérience. Je suis tombé dans la marmite étant petit, mon père avait acheté un Apple – avant même ma naissance (oui ça date !). Et maintenant je me passionne essentiellement pour tout ce qui est du monde Java et du système, les OS open source en particulier.

Au quotidien, je suis devops, bref je fais du dév, je discute avec les opérationnels, et je fais du conseil auprès des clients.

Son Twitter Son LinkedIn

Laisser un commentaire