Les Web Servlet Filters et le cryptage -1ère partie

L'objectif du billet est d'expliquer par des exemples concrets la notion de « Filter » (ou Web Filter, en français Filtre).
Pour vraiment comprendre la notion de « Servlet Filter » il faudrait les voir en action sur des exemples réels.
C'est justement ce que nous présentons ci-dessous.
La première partie de ce billet monte une démo simple mais très utile car elle détaille la création d'une « Servlet Filter » qui transforme la requête HTTP POST en simulant le cryptage du mot de passe.

Aucun pré-requis n'est exigé pour cette partie.

Dans la seconde partie, la démo s'intéresse à l'intégration du « Filter » à une application Web existante
basée sur spring-security 2.x. Plus précisément, la « Servlet Filter » agit sur les « Filtres » de spring-security.

Cette seconde partie, d'un niveau très avancé, est réservée à ceux connaissant parfaitement spring et spring-security.

Ce qui est commun entre les deux parties est que nous partons d'une application Web existante et nous ajoutons les transformations utiles avec le filtre sans modifier le code java/jsp. Seul le fichier descripteur « web.xml » sera mis à jour.

NOTE. Les dépendances nécessaires seront indiquées.

Commençons par répondre aux questions fondamentales suivantes.

Question1. C'est quoi une servlet Filter?

Les spécifications Servlet v2.3 introduit un nouvel composant nommé «Servlet Filter» qui permet d'intercepter dynamiquement les requêtes et les réponses des méthodes HTTP Get/Post afin de les transformer (pré ou post-traitement).
La servlet «Filter» diffère d'une servlet standard car elle ne produit pas de réponse à une requête.

NOTA. L'interception d'un flux (stream ouvert) lié à une requête/réponse n'est pas chose aisée pour des raisons, entre autres, de sécurité. L'encapsulation des objet de type ServletRequest garantit que les paramètres de la requête ne seront pas altérés.
C'est pourquoi des règles strictes sont à observer. Le seul moyen de transformer la requête (ou la réponse) est d'envelopper (d'emballer) la requête/réponse interceptée dans une nouvelle classe «Wrapper» qui hérite de HttpServletRequestWrapper ou de HttpServletResponseWrapper.

fig1_tb_filters.gif

Définition du filter extraite du site Oracle:
The Java Servlet specification version 2.3 introduces a new component type, called a filter.
A filter dynamically intercepts requests and responses to transform or use the information
contained in the requests or responses. Filters typically do not themselves create responses, but
instead provide universal functions that can be "attached" to any type of servlet or JSP page.

Question2. Comment écrire un nouveau Filter?

La «servlet Filter» doit implémenter l'interface javax.servlet.Filter qui déclare trois méthodes permettant de greffer les transformations. Ces méthodes (toutes void) sont :

doFilter(HttpServletRequest rq,HttpServletResponse rp,FilterChain ch)
throws ServletException,IOException;
init(FilterConfig fConfig) throws ServletException;
destroy();

La méthode principale est «doFilter». Elle centralise le code des transformations voulues, et c'est ici que le chaînage doit être réalisé avec l'appel suivant ch.doFilter(req,rep) ce qui permet de passer la main aux autres Filtres jusqu'à la servlet ou la jsp finale.

Passons à la pratique.

Développons d'abord un exemple simple et pratique.

Mais avant, préparons ensemble l'environnement en commençant par le projet Web (existant) avant de lui rajouter le Filter.

1- Créer sous eclipse, un projet «Dynamic Web Project» nommé «demo1». Indiquer « Tomcat » comme serveur conteneur servlet.

2- Créer une servlet nommée MyServlet dont le code se résume à:

 [java]
	final String NAME="j_username";
	final String PWD="j_password";
    	public MyServlet() {
        super();
    	}
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
	throws ServletException, IOException {
		doPost(req, resp);
				
	}
	protected void doPost(HttpServletRequest req,HttpServletResponse resp) 
	throws ServletException, IOException {
		String nom=req.getParameter(NAME);
		String pwd=req.getParameter(PWD);
		
		PrintWriter pwriter=resp.getWriter();
		pwriter.println("<h1>nom="+nom+", pwd="+pwd);
		
	}

La page jsp nommé «login.jsp» qui contient le formulaire de saisie du couple nom/password. Le code jsp est :

 [jsp]
   <html>
   <body>
   <form action="MyServlet" method="post">
   <label>Nom : </label><input id="j_username" name="j_username" value="TOTO"/>
   <label>Identifiant : </label><input type="password" id="j_password"  
    name="j_password" value="TOTOLEVRAI"/>
   <input type="submit" value="ok">
   </form>
   </body></html> 

Notez l'action du formulaire (balise <form>) est MyServlet.
Et les deux champs de saisie sont nommés respectivement 'j_username' et 'j_password'.

Testons que tout fonctionne bien en saisissant dans le navigateur l'Url :
http://localhost:8080/demo1/login.jsp.

Saisir le nom 'toto' et son password 'TOTOLEVRAI' et MyServlet affiche ceci :
nom=toto, pwd=TOTOLEVRAI

Voici donc l'application Web existante.

Elle est bien simple et le contenu de son descripteur « web.xml » est, pour l'instant, bien maigre :

 [xml]
   <servlet>
    <description></description>
    <display-name>MyServlet</display-name>
    <servlet-name>MyServlet</servlet-name>
    <servlet-class>[NOM_PACKAGE].MyServlet</servlet-class>
   </servlet>
   <servlet-mapping>
    <servlet-name>MyServlet</servlet-name>
    <url-pattern>/MyServlet</url-pattern>
   </servlet-mapping>
    <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
   </welcome-file-list>
  

Attention, il faudrait compléter ce fichier avec le nom de votre package de MyServlet ci-dessus.

Pour la suite, supposons que notre client exige de ne pas afficher ni stocker en clair les mots de passe.

Évidemment c'est ici le «Filter» entre en jeu.

Ecrivons une classe Servlet Filter, nommée MyFilter.
Celle-ci va s'appuyer sur une autre, nommée MyRequestWrapper qui hérite de HttpServletRequestWrapper et qui permet d'envelopper la requête en entrée et lui greffer les transformations utiles.

L'idée intéressante est que, dans MyRequestWrapper, la méthode String getParameter (String param) définie dans la classe mère est surchargée.
Le décorateur assure ainsi que l'application Web existante continue à fonctionner correctement alors que la valeur du paramètre "j_pasword" a été transformé.
En principe, d'autres méthodes sont à surcharger mais pour notre démo cela est suffisant.


La création de ce «Filter» se fait en trois étapes :

Étape 1. Écrire la classe enveloppe MyRequestWrapper.java :

 [java]
public class MyRequestWrapper extends HttpServletRequestWrapper {

	public MyRequestWrapper(HttpServletRequest request) {
		super(request);
	}
	//surcharger la méthode getParameter
	public String getParameter(String param){
		if(param.equals("j_password")){
			return "Xyuty/Rcryptedendur!=";
		}else{
			return super.getParameter(param);
            }
	}
  }

Étape 2. Écrire la classe Filter nommé «MyFilter» dont voici son contenu :

 [java]
 import java.io.IOException;
 import javax.servlet.*;

 public class MyFilter implements Filter {

   private FilterConfig fConfig;
   public MyFilter() {
        super();
    }
   public void doFilter(ServletRequest req,ServletResponse resp,FilterChain ch) 
   throws IOException, ServletException {
	String pwd=(String) request. getParameter("j_password");
	if (req instanceof HttpServletRequest && pwd!=null ){
	  MyRequestWrapper requestWrapper=new 
	  MyRequestWrapper((HttpServletRequest)req);
	  ch.doFilter(requestWrapper, resp);
	}else{
	   // pass the request along the filter chain
	   ch.doFilter(request, response);
	 }
	}
	public void init(FilterConfig fConfig) throws ServletException {
		this.fConfig=fConfig;
	}
	public void destroy() {fConfig=null;}
 }

Étape 3. Compléter le fichier «web.xml» afin d'ajouter la déclaration du Filter comme suit :

<filter>
  <filter-name>myFilter</filter-name>
   <filter-class>[NOM_PACKAGE].MyFilter</filter-class>
  </filter>
  <filter-mapping>
   <filter-name>myFilter</filter-name>
   <url-pattern>/*</url-pattern>
  </filter-mapping>

Adaptez ce fichier avec le nom de votre package et relancer l'Url précédente dans le navigateur, le résultat affiché est :

nom=toto, pwd=Xyuty/Rcryptedendur!=

Voilà la première démo est terminée. Les étapes nécessaires de création d'une «Servlet Filter» permettant d'effectuer des transformations du flux de la requête sont explicitées.

Donc, nous avons simulé le cryptage du mot de passe avec une chaîne écrite en dur. Nous aurions dû appeler une méthode qui effectue réellement le cryptage. C'est ce que nous ferons dans la seconde partie.

8 commentaires

  1. C’est pour cela que « = new self(); »
    est ajouté après « private static $_instance »
    L’instanciation de l’objet se fait donc au premier appel de la classe.

  2. Crypter sert à répondre à une demande du client de ne pas stocker les mots de passe en clair dans une base de données ce qui renforce la sécurité. C’est même une norme chez certains clients (institutions)

  3. Merci pour ta réponse. Le cas que tu cite (BDD) ne m’est pas passé par la tête (généralement, je pense LDAP)

  4. Certaines situations particulières sont gérés avec LDAP et stockage en base. Mais le plus important, est que LDAP n’est pas fait pour gérer les internautes qui s’abonnent à notre site/application!:)

  5. Pa tout à fait d’accord. LDAP est un moyen standard d’authentifier un utilisateur (indépendant du langage) alors qu’avec une BDD, on est obligé de connaitre le modèle de données.

    Ceci dit, la quasi-totalité des applications disposent d’une BDD (y ajouter des informations d’authentification/authorisation est plus simple).

    Je dirais également que LDAP est plus adapté aux applications d’entreprise can on y capitalise tout (utilisateurs, ressources…etc)

  6. Je crois que ma remarque n’a pas été bien comprise. Je dois la reformuler
    mais là je n’ai pas le temps!

  7. Merci de partager votre article. J’ai vraiment aimé ça. J’ai mis un lien vers mon site pour ici, donc d’autres personnes peuvent le lire. Mes lecteurs ont sur ​​les interets mêmes. Merci pour partager le poste belle m’amener!

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Captcha *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.