Skip to content

Conteneur WEB

Présentation

Généralités

Un conteneur est un programme Java qui est respecte la norme Java EE. Il y a deux grandes familles de conteneurs :

  1. conteneurs full Java EE (JBoss, Glassfish, WebSphere, etc.)
  2. conteneurs légers dit servlet container (Tomcat, Winstone, etc.)

A noter qu'un conteneur full java EE intègre également les fonctions d'un conteneur léger.

Une Servlet est

  • une classe Java qui implémente l'interface javax.servlet.Servlet:
    • par implémentation directe (rarement)
    • par héritage d'une des sous-classes disponibles dans l'implémentation Java EE
      • GenericServlet -- Servlet générique non liée au protocole HTTP
      • HttpServlet -- Classe abstraite pour les servlets HTTP
      • FacesServlet -- Classe pour les applications utilisant JavaServer Faces
  • une application qui s'exécute sur un serveur en réponse à une requête.

Apache Tomcat

Nous utiliserons le projet Tomcat de la fondation Apache :

  • Projet Open Source de la fondation Apache
  • Implémente les servlets9
  • Implémente les JSP (JavaServer Pages)10
  • Héberge un serveur HTTP (pour des questions de 'tout-en-un' mais est adossé à un serveur de production type Apache et peut alors être raccordé en proxy ou en protocole JK, pour Jakarta)

Principe de fonctionnement des servlets

Une servlet est une classe Java utilisée pour étendre les capacités du serveur. Dans notre cas, elles sont accessibles par le biais de requêtes HTTP.

Le développeur n'a pas à gérer le cycle de vie d'une servlet, c'est le conteneur qui s'en charge.

  • Instanciation (pool de démarrage)
  • Initialisation (appel à la méthode init())
  • Démarrage des threads d'écoute (du processus de la JVM)

Cycle de vie d'une servlet

Une servlet n'est instanciée qu'une seule fois. Ce sont les threads de la JVM qui rendent possible la gestion des appels concurrents. Selon les sollicitations, le conteneur appelle alors les méthodes d'implémentation des servlets correspondantes au protocole HTTP :

  • doGet pour le GET
  • doPost pour le POST
  • etc.

Les instances de servlets sont conservées d'un appel à l'autre et les écueils de la programmation multi-threads doivent être manipulés avec précaution :

  • stockage d'états dans des variables membres (stateless vs. statefull)
  • gestion de sous-threads
  • durée de rétention
  • pseudo singletons
  • et bien d'autres encore...

En effet, la servlet peut être partagée avec plusieurs threads.

HelloWorld

Distribution d'une application WEB

Pour rappel, les applications Java SE sont diffusées à l'aide d'une archive .jar qui inclue les classes compilées de l'application et un fichier de méta-données MANIFEST.MF contenant entre autre :

  • le nom du la classe principale (entrée Main-Class)
  • la liste des noms dépendances (entrée Class-Path)

Les applications Java EE sont également diffusées à l'aide d'une archive. Celle-ci est au format WAR (Web application ARchive) et est ensuite manipulée par le conteneur. Comme le .jar, le .war est une archive compressée au format .zip. Le contenu est normalisé pour n'importe quel conteneur :

  • fichiers statiques ou ressources à partir de la racine (accessibles directement)
  • WEB-INF (espace réservé au conteneur)

    - web.xml, qui décrit l'application (par analogie au MANIFEST.MF, mais dont le contenu est également normalisé) - classes, qui contient toutes les classes compilées de l'application (format .class) - lib qui contient les dépendances (les vrais fichiers, pas les chemins) de l'application

Dans tous les cas, les dépendances d'implémentation de la spéc. Java EE, propre au conteneur, sont stockées ailleurs dans l'arborescence du serveur, pas dans l'application... Attention donc à vérifier le scope dans les dépendances Maven. 12

Une application Java packagée en .jar peut être lancée par n'importe quelle JVM, n'importe quelle application packagée en .war peut être déployée dans n'importe quel conteneur Java EE... moyennant d'éventuels ajouts spécifiques au conteneur, distincts toutefois de l'application.

HelloWorld pour une servlet

HelloServlet.java, la servlet

C'est une classe Java qui doit respecter le contrat Servlet.

  • Elle récupère les informations fournies lors d'une requête HTTP (voir le paramètre request dans la signature des méthodes doGet)
  • Elle écrit la réponse de la requête HTTP (voir le paramètre response dans la signature des méthodes doGet)
  • Elle peut générer du code HTML (ou n'importe quoi d'autre) \pagebreak
import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class HelloServlet extends HttpServlet {

  @Override
  protected void doGet(HttpServletRequest request,
      HttpServletResponse response)
              throws ServletException, IOException {
    PrintWriter out = response.getWriter();
    out.println( "<HTML>" );
    out.println( "<HEAD>");
    out.println( "<TITLE>Page generee par une servlet</TITLE>" );
    out.println( "</HEAD>" );
    out.println( "<BODY>" );
    out.println( "<H1>Hello world !</H1>" );
    out.println( "</BODY>" );
    out.println( "</HTML>" );
    out.close();
  } 
}

Sortie

<HTML>
<HEAD>
<TITLE>Page generee par une servlet</TITLE>
</HEAD>
<BODY>
<H1>Hello world !</H1>
</BODY>
</HTML>

Le descripteur de déploiement WEB-INF/web.xml

C'est LE fichier névralgique de l'archive. Le fichier web.xml fournit des informations de configuration et de déploiement pour les composants d'une application Web. Deux sections sont particulièrement importantes :

  1. <servlet\> qui déclare auprès du conteneur les classes de Servlet mises à disposition
  2. <servlet-mapping\> qui indique au conteneur quelle URL affecter à quelle servlet
<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
    <display-name>Archetype Created Web Application</display-name>
    <servlet>
        <servlet-name>HelloServlet</servlet-name>
        <servlet-class>fr.iut.HelloServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>HelloServlet</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>
</web-app>

Déroulement d'une requête HTTP

Mécanisme requête HTTP


Exercice

Objectif : Comprendre l'utilisation d'une servlet et créer une application web à l'aide de Maven (30 minutes)

  1. Dans votre dépôt, construisez une application WEB helloServlet :
mvn archetype:generate -DgroupId=fr.iut \ 
  -DartifactId=helloServlet \ 
  -DarchetypeArtifactId=maven-archetype-webapp \
  -DinteractiveMode=false
  1. Importer le projet dans IntelliJ
  2. Ajouter dans le pom.xml la dépendance vers la librairie Java EE (pour connaître les classes de base des servlets) :
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
  <scope>provided</scope>
    <!-- pour compilation / non inclu dans l'archive générée -->
</dependency>
  1. Ajouter dans le pom.xml les informations pour compiler avec une version de java précise
  <properties>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>
  1. Rafraîchir le projet si nécessaire pour que IntelliJ utilise la nouvelle dépendance
  2. Créer une servlet fr.iut.HelloServlet accessible depuis le chemin /hello qui affiche un message de bienvenue (pensez à créer le dossier src/main/java)

    1. Surchargez la méthode doGet 2. Réaliser une implémentation inspirée de l'extrait de code précédent

  3. Créer le fichier web.xml dans le dossier src/main/webapp/WEB-INF/ en s'inspirant de l'extrait de code précédent

  4. Renommer le fichier index.jsp en index.html
  5. Remplacer le contenu de index.html par
<html>
<head>
    <meta http-equiv="Refresh" content="0; URL=hello"/>
</head>
<body></body>
</html>
  1. Packager le projet avec Maven : mvn package (et s'assurer du résultat dans le dossier target/)
  2. Commiter et pousser

Apache Tomcat

Installation

Tomcat est relativement léger, vous pouvez l'installer sur votre compte ou dans le répertoire /tmp. Mode opératoire d'installation :

  1. Télécharger les binaires de la version 9.058 (.tar.gz sous linux ou .zip sous Windows): https://tomcat.apache.org/download-90.cgi

  2. Désarchiver dans le dossier de votre choix

  3. Pour les démarrages en ligne de commande, déclarer une variable d'environnement CATALINA_HOME (éviter les espaces dans les chemins)

  4. * linux export CATALINA_HOME=~/opt/apache-tomcat-9.0.58 * windows set CATALINA_HOME="C:\opt\apache-tomcat-9.0.58"

  5. Pour les déploiements par la console web, ajouter un administrateur dans le fichier apache-tomcat-9.0.58/conf/tomcat-users.xml:

<?xml version='1.0' encoding='utf-8'?>
<tomcat-users xmlns="http://tomcat.apache.org/xml"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd"
              version="1.0">
<user username="admin" password="admin" roles="manager-gui"/>
</tomcat-users>
1. Tester l'installation en démarrant (voir ci-après) et corriger si nécessaire

Démarrer Tomcat

Tomcat est une application Java. C'est un serveur/demon dans le sens où il est prévu pour fonctionner en tâche de fond sans interaction humaine. On distinguera le démarrage en mode production du mode foreground, adapté au développement. Il est possible que vous deviez modifier les modes d'accès au fichier catalina.sh (script shell) : chmod 544 catalina.sh.

catalina.sh (ou .bat) pour le mode développement

$ cd $CATALINA_HOME/bin
$ ./catalina.sh 
Using CATALINA_BASE:   /mnt/roon/users/cjohnen/opt/apache-tomcat-9.0.4
Using CATALINA_HOME:   /mnt/roon/users/cjohnen/opt/apache-tomcat-9.0.4
Using CATALINA_TMPDIR: /mnt/roon/users/cjohnen/opt/apache-tomcat-9.0.4/temp
Using JRE_HOME:        /opt/oracle_jdk
Using CLASSPATH:       /mnt/roon/users/cjohnen/opt/apache-tomcat-9.0.4/bin/bootstrap.jar:/mnt/roon/users/cjohnen/opt/apache-tomcat-9.0.4/bin/tomcat-juli.jar
Usage: catalina.sh ( commands ... )
commands:
  debug             Start Catalina in a debugger
  debug -security   Debug Catalina with a security manager
  jpda start        Start Catalina under JPDA debugger
  run               Start Catalina in the current window
  run -security     Start in the current window with security manager
  start             Start Catalina in a separate window
  start -security   Start in a separate window with security manager
  stop              Stop Catalina, waiting up to 5 seconds for the process to end
  stop n            Stop Catalina, waiting up to n seconds for the process to end
  stop -force       Stop Catalina, wait up to 5 seconds and then use kill -KILL if still running
  stop n -force     Stop Catalina, wait up to n seconds and then use kill -KILL if still running
  configtest        Run a basic syntax check on server.xml - check exit code for result
  version           What version of tomcat are you running?
Note: Waiting for the process to end and use of the -force option require that $CATALINA_PID is defined

startup.sh (ou .bat) pour le mode serveur

également disponible en faisant ./catalina.sh start

$ ./startup.sh
Using CATALINA_BASE:   /mnt/roon/users/cjohnen/opt/apache-tomcat-9.0.4
Using CATALINA_HOME:   /mnt/roon/users/cjohnen/opt/apache-tomcat-9.0.4
Using CATALINA_TMPDIR: /mnt/roon/users/cjohnen/opt/apache-tomcat-9.0.4/temp
Using JRE_HOME:        /opt/oracle_jdk
Using CLASSPATH:       /mnt/roon/users/cjohnen/opt/apache-tomcat-9.0.4/bin/bootstrap.jar:/mnt/roon/users/cjohnen/opt/apache-tomcat-9.0.4/bin/tomcat-juli.jar
Tomcat started.

$

Vous y avez accès à l'adresse : http://127.0.0.1:8080/

Éteindre Tomcat

Trois façons d'éteindre Tomcat :

  1. Ctrl-C : directement en stopant la JVM... suffisant pour le développement, mais termine brutalement l'exécution
  2. $ $CATALINA_HOME/bin/catalina.sh stop
  3. avec shutdown.sh :
    $ cd $CATALINA_HOME/bin
    $ ./shutdown.sh
    

Déploiement d'applications dans Tomcat

Les opérations à effectuer lors d'un déploiement dans un conteneur Java EE ne sont pas normées. Chaque conteneur a son propre mode de fonctionnement.

Dans Tomcat, on peut déployer les applications selon deux manières :

  1. graphiquement, par console (web) d'administration, l'installation des application se fait à l'aide du formulaire approprié.
  2. manuellement, en copiant/collant l'archive dans le dossier spécifique de déploiement

Pour faciliter le travail, l'utilisation de liens symboliques permet d'éviter de nombreux copier/coller :-)

Déploiement par l'application web manager

L'URL d'accès à la gestion des applications contenues dans Tomcat est http://localhost:8080/manager/.

Pour des raisons de sécurité, vous devez utiliser l'identifiant/mot de passe précédemment spécifié pour y accéder.

Interface de gestion d'applications Tomcat


Exercice

Objectif : Utiliser et configurer Tomcat. Installer Apache Tomcat et déployer l'application HelloServlet (10 minutes)

  1. Packager le projet avec Maven : mvn package (et s'assurer du résultat dans le dossier target/)
  2. Démarrer Tomcat depuis la console $CATALINA_HOME/bin/catalina.sh run
  3. Déployer l'application dans Tomcat à l'aide du gestionnaire d'applications Web de Tomcat

    1. Aller sur l'URL http://localhost:8080/manager/ 2. Choisir le Fichier WAR à déployer 3. Testez le fonctionnement

      - de la servlet : <http://localhost:8080/helloServlet/hello>
      - de la redirection automatique : <http://localhost:8080/helloServlet/>
    

Alternatives aux servlets

- Les servlets doivent construire manuellement la réponse à la requête HTTP (avec des prints) - Leur écriture peut donc être rébarbative et sujette à erreur - Différentes techniques permettent d'utiliser un formalisme intermédiaire qui :

 - soit génère à l'exécution une servlet à partir d'un langage de description (JSP[^jsp])
 - soit interprète un langage de description à l'aide d'une servlet existante (JSF[^jsf])

- ... afin de simplifier le développement de la partie graphique d'une application

Exemple JSF - JavaServer Faces

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:f="http://java.sun.com/jsf/core"      
      xmlns:h="http://java.sun.com/jsf/html">

    <h:head>
        <title>JSF 2.0 Hello World</title>
    </h:head>
    <h:body>
        <h3>JSF 2.0 Hello World Example - hello.xhtml</h3>
        <h:form>
           <h:inputText value="#{helloBean.name}"></h:inputText>
           <h:commandButton value="Welcome Me" action="welcome">
       </h:commandButton>
        </h:form>
    </h:body>
</html>

- La courbe d'apprentissage de JSF est relativement longue - Le langage est très évolué - manipulation des Beans à l'aide du langage EL - Pour des raisons historique, qui ne sont plus justifiées, JSF n'est pas utilisé intensivement dans l'industrie - Nous ne l'utiliserons pas en TD

Exemple JSP - JavaServer Pages

Le JavaServer Pages ou JSP est une technique basée sur Java qui permet aux développeurs de créer dynamiquement du code HTML, XML ou tout autre type de page web.

<html>
<head><title>First JSP</title></head>
<body>
  <%
    double num = Math.random();
    if (num > 0.95) {
  %>
      <h2>You'll have a luck day!</h2><p>(<%= num %>)</p>
  <%
    } else {
  %>
      <h2>Well, life goes on ... </h2><p>(<%= num %>)</p>
  <%
    }
  %>
  <a href="<%= request.getRequestURI() %>"><h3>Try Again</h3></a>
</body>
</html>

- La courbe d'apprentissage de JSP est relativement simple - Le langage est minimaliste - Syntaxe proche de PHP - Nous l'utiliserons dans les TD

Frameworks MVC

JSP comme vue de servlet

Les servlets sont à la base de tout développement web en Java EE. Pour résumer, on pourrait dire que c'est du code HTML dans du Java. À l'usage, il s'est rapidement avéré que le mélange n'était pas adapté aux conditions réelle de développement : un web designer n'est pas à l'aise dans du code !

Très vite, la technologie JSP (JavaServer Pages) a tenté de résoudre la problématique. Une JSP est une servlet à l'envers, c'est à dire que c'est du code HTML qui contient des balises spécifiques.

Rappel de l'exemple précédent :

<html>
<head><title>First JSP</title></head>
<body>
  <%
    double num = Math.random();
    if (num > 0.95) {
  %>
      <h2>You'll have a luck day!</h2><p>(<%= num %>)</p>
  <%
    } else {
  %>
      <h2>Well, life goes on ... </h2><p>(<%= num %>)</p>
  <%
    }
  %>
  <a href="<%= request.getRequestURI() %>"><h3>Try Again</h3></a>
</body>
</html>

À la première invocation, le conteneur détecte que c'est une JSP qui est demandée et génère à la volée du code source Java puis le compile afin de faire le rendu.

L'apparition des JSPs a cette fois mécontenté les développeurs qui se sont vite rendu compte des difficultés de maintenance du code ainsi produit, le manque d'IDE (integrated development environment) n'ayant pas arrangé les choses.

Les Tags, ou taglibs, sont alors venus réconcilier les deux mondes. Un tag est une balise à insérer dans une JSP. Cette balise est reliée à du code Java, séparant ainsi clairement les deux mondes.

MVC à la rescousse

Très vite, les patterns ont été mis en place pour favoriser la maintenabilité du code source des applications. MVC, Model View Controller a très vite rencontré un franc succès, d'autant plus que les initiatives open source ont pris de cours les industriels des JSR (Java Specification Requests).

Dans les deux cas, l'idée est de séparer la logique de rendu (la vue), de la logique de récupération des données (le modèle) de la logique de contrôle du flux des demandes (le controlleur). Une fois mis en place, l'architecture MVC permet de remplacer une technologie d'un composant par une autre. L'exemple le plus courant est de remplacer les JSPs par un moteur de templating générique, type Freemarker13 ou Velocity14.

MVC modèle 1

MVC modèle 1

Un controleur par vue.

MVC modèle 2

MVC modèle 2

Un controleur qui redirige les requêtes à d'autres controleurs spécialisés.

Liste de frameworks à connaître

Ceci n'est pas une liste exhaustive. Chaque framework tend à améliorer ou résoudre une problématique du web (portabilité du code, rapidité de développement, dette technique, etc.)... Mais tous ces frameworks s'appuient sur des servlets et reposent sur les mêmes principes !

  • JSTL, Javaserver page Standard Tag Library, bibliothèque de Tags
  • Struts v.1, a eu beaucoup de succès car répondant aux problématiques
  • Struts v.2, moins verbeux que la précédente version
  • Spring MVC, évolution du framework d'injections de dépendances éponyme
  • JSF, JavaServer Faces, la version normalisée par les JSR, mais qui s'est fait prendre de vitesse par les initiatives open source

HelloWorld template Freemarker

Objectif : Manipuler un framework de templating, Freemarker dans une servlet (30 minutes)

Ressource d'aide : 1

  1. Ajouter la dépendance de Freemarker dans le pom.xml :
        ...
<dependency>
  <groupId>org.freemarker</groupId>
  <artifactId>freemarker</artifactId>
  <version>2.3.20</version>
</dependency>
        ...
  1. Ajouter une nouvelle classe de servlet, cette fois, déclarée par annotation (plutôt que dans le fichier web.xml)
package fr.iut;

import freemarker.template.DefaultObjectWrapper;
import freemarker.template.Template;
import freemarker.template.TemplateException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@WebServlet(name = "home", urlPatterns = {"/home"})
public class HomeServlet extends HttpServlet {

  @Override
  protected void doGet(HttpServletRequest request,
          HttpServletResponse response)
                    throws ServletException, IOException {
  Template freemarkerTemplate = null;
  freemarker.template.Configuration freemarkerConfiguration =
  new freemarker.template.Configuration();
  freemarkerConfiguration.setClassForTemplateLoading(this.getClass(), "/");
  freemarkerConfiguration.setObjectWrapper(new DefaultObjectWrapper());
  try {
      freemarkerTemplate =
          freemarkerConfiguration.getTemplate("templates/home.ftl");
  } catch (IOException e) {
    System.out.println("Unable to process request, 
            error during freemarker template retrieval.");  }

  Map<String, Object> root = new HashMap<String, Object>();
        // navigation data and links
  root.put("title", "freemarker Servlet");
  root.put("now",
            SimpleDateFormat.getDateTimeInstance().format(new Date()));
  PrintWriter out = response.getWriter();
  assert freemarkerTemplate != null;
  try {
      freemarkerTemplate.process(root, out);
    out.close();} 
  catch (TemplateException e) { e.printStackTrace(); }
        // set mime type
  response.setContentType("text/html");
  }
}

1. Ajouter le dossier templates aux resources du projet 1. Ajouter le template freemarker home.ftl

<!DOCTYPE html>
<html lang="fr">

<head>
  <title>${title}</title>
</head>
<body>
<h2>${now}</h2>
</body>
</html>
  1. Packager et Déployer l'application dans Tomcat à l'aide du gestionnaire d'applications Web de Tomcat 1. Aller sur l'URL http://localhost:8080/manager/ 1. Choisir le Fichier WAR à déployer 1. Testez le fonctionnement

  2. Créer un autre servlet affichant une liste d'objets de taille quelconque en utilisant le template freemarker listRoom.ftl ou un template similaire

<!DOCTYPE html>
<html lang="fr">
<meta charset="utf-8"/>
<head>
    <title>${title}</title>
</head>
<body>
<h2> list of FakeRooms</h2>
<p></p>
<ul>
    <#list fakeRooms as room>
        <li>
            ${room.name} : ${room.occupation} / ${room.capacity}
        </li>
    </#list>
</ul>
</body>
</html>

Exemple de Servlet affichant une liste

  1. Commiter et pousser

QR Code avec zxing

Les QR Code sont devenus incontournables dans les applications modernes.

Objectif : Intégrer la librairie de génération de QR Codes zxing (30 minutes)

Exemple de QR Code

  1. Ajouter les dépendances de zxing dans le pom.xml
    ...
<!-- QR Code management -->
<dependency>
  <groupId>com.google.zxing</groupId>
  <artifactId>core</artifactId>
  <version>3.2.0</version>
</dependency>
<dependency>
  <groupId>com.google.zxing</groupId>
  <artifactId>javase</artifactId>
  <version>3.2.0</version>
</dependency>
...
  1. Rajouter la servlet de génération de QR

@WebServlet(name = "qrcode", urlPatterns = {"/qrcode"})
public class QRCodeServlet extends HttpServlet {
  @Override
  protected void doGet(final HttpServletRequest request, 
    final HttpServletResponse response) 
        throws ServletException, IOException {
    QRCodeWriter writer = new QRCodeWriter();
  BitMatrix bitMatrix = null;
  try {
    String url = "coucou";
    bitMatrix = writer.encode(url, BarcodeFormat.QR_CODE, 300, 300);
  } catch (WriterException e) { e.printStackTrace(); }
  response.setContentType("image/png");
  MatrixToImageWriter.writeToStream(bitMatrix, "png", 
      response.getOutputStream());
  }
}
1. Commiter et pousser. 2.


Authentification

  • Comme dans toute application de type entreprise, il est nécessaire d'authentifier les utilisateurs.
  • En effet ils ne peuvent pas effectuer toutes les actions possibles (i.e. appeler toutes les méthodes de tous les objets).
  • Les conteneurs Java EE permettent donc d'utiliser différentes méthodes d'authentification.

Plusieurs couches de technologies intervienent dans le mécansime d'authentification.

Contraintes de sécurité

  • Les contraintes de sécurité permettent de faire un mapping entre une URL et des privilèges d'accès
  • Utilisation à l'aide d'annotations @HttpConstraint ou @HttpMethodConstraint si on utilise des servlets.
// Seuls les utilisateurs avec le rôle adminApp ont accès à la servlet
@WebServlet("/manage")
@ServletSecurity(@HttpConstraint(rolesAllowed = "adminApp"))
public class AdminServlet extends HttpServlet {
      // servlet code...
}


// Seuls les utilisateurs avec le rôle adminApp peuvent faire du GET et du POST
// Le POST est nécessairement sur une connexion chiffrée
@WebServlet("/manage")
@ServletSecurity(
  httpMethodConstraints = {
    @HttpMethodConstraint(value = "GET", rolesAllowed = "adminApp"),
    @HttpMethodConstraint(value = "POST", rolesAllowed = "adminApp",
                transportGuarantee = TransportGuarantee.CONFIDENTIAL),      
  }
)
public class AdminServlet extends HttpServlet {
    // servlet code...
}

- Ou bien à l'aide du fichier web.xml et de différentes directives que l'on utilise des servlets ou pas (security-constraint, web-resource-collection, auth-constraint, user-data-constraint2

<!-- SECURITY CONSTRAINT #1 -->
<security-constraint> <!-- spécifie l'utilisation de contraintes -->
    <web-resource-collection> <!-- URLs concernées -->
        <web-resource-name>wholesale</web-resource-name> <!-- nom -->
        <url-pattern>/company/wholesale/*</url-pattern> <!-- motif -->
    </web-resource-collection>
    <auth-constraint> <!-- contrainte d'authentification (qui peut) -->
        <role-name>PARTNER</role-name>
    </auth-constraint>
    <user-data-constraint> <!-- contrainte de transport (chiffrement) -->
        <transport-guarantee>CONFIDENTIAL</transport-guarantee>
    </user-data-constraint>
</security-constraint>

<!-- SECURITY CONSTRAINT #2 -->
<security-constraint>
    <web-resource-collection>
        <web-resource-name>retail</web-resource-name>
        <url-pattern>/company/retail/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <role-name>CLIENT</role-name>
    </auth-constraint>
    <user-data-constraint>
        <transport-guarantee>CONFIDENTIAL</transport-guarantee>
    </user-data-constraint>
</security-constraint>
  • EJB3 peux gérer les contraintes de sécurité de façon plus fine (@PermitAll, @DenyAll, @RolesAllowed, @DeclareRoles, @RunAs)

Modalités d'authentification

- Afin de gérer les accès, il est nécessaire que le conteur web dispose d'un mécanisme d'authentification. - Plusieurs modes d'authentification sont disponibles :

- *Basic authentication* : le serveur demande un identifiant/mot de passe au navigateur
- *Form-based authentication* : le développeur de l'application peut proposer un formulaire intégré dans son application (*i.e.*, page d'authentification)
- *Digest authentication* : similaire à Basic mais sans faire transiter le mot de passe en clair
- *Client authentication* : le serveur authentifie le client grâce à un certificat (comme bitbucket par ssh)
- *Mutual authentication* : le client authentifie le serveur grâce à un certificat puis le serveur authentifie le client grâce à un certificat (ou un identifiant mot de passe)

- Les modalités d'authentification sont précisées dans le fichier web.xml :

<!-- Authentification par formulaire -->
<login-config>
    <auth-method>FORM</auth-method>
    <realm-name>file</realm-name>
    <form-login-config>
        <form-login-page>/login.xhtml</form-login-page>
        <form-error-page>/error.xhtml</form-error-page>
    </form-login-config>
</login-config>

<!-- Authentification de type Digest -->
<login-config>
    <auth-method>DIGEST</auth-method>
</login-config>

Gestion des utilisateurs et des rôles

Rôles

La liste des rôles requis pour utiliser l'application est listée de la façon suivante dans le web.xml:

<security-role>
    <role-name>manager</role-name>
</security-role>
<security-role>
    <role-name>employee</role-name>
</security-role>

Royaume

  • Les utilisateurs appartiennent à un royaume (realm)3
  • Chaque utilisateur est associé à une liste de rôles
  • La déclaration des contraintes de sécurité est normalisée, mais pas le mécanisme d'accès aux utilisateurs
  • Tomcat propose les méthodes suivantes :

    - JDBCRealm : informations stockées dans une base de données relationnelle accédée par JDBC - DataSourceRealm : informations stockées dans une base de données relationnelle accédée par une ressoource de type JNDI JDBC DataSource. - JNDIRealm : informations stockées dans un serveur LDAP - UserDatabaseRealm : informations stockées dans une ressource UserDatabase JNDI - MemoryRealm : informations stockées dans une collection en mémoire, intialisée depuis un document XML (conf/tomcat-users.xml). À ne pas utiliser en production - JAASRealm - informations récupérées depuis le framework Java Authentication & Authorization Service (JAAS)

  • Il est possible de créer son propre connecteur pour utiliser d'autres sources de données

Exercice

Objectif : Installer une authentification simple et créer un formulaire en utilisant Freemarker, JSP ou HTML.

  • Créer un formulaire permettant la saisie d'un nom. La balise form doit contenir les attributs suivants : method="POST" action="j_security_check". Les champs de nom et mot de passe doivent avoir respectivement l'atrtibut name à la valeur j_username et j_password.
  • Ajouter un utilisateur avec le rôle application.
  • Sécuriser une servlet (nouvelle ou existante) en autorisant les utilisateurs du rôle application.
  • Installer une authentification simple (memoryRealm Tomcat) sur le formulaire. L'authentification sera assurée à l'aide de Java EE et du conteneur Tomcat.
  • Commiter et pousser

Injection de dépendences

- Contrairement aux conteneurs Full Java EE, les conteneurs WEB de type Tomcat n'hébergent pas de bibliothèque d'injection de dépendences - Il est donc nécessaire d'importer une implémentation dans le .war - Nous choisissons Google Guice ; d'autres librairies ont un fonctionnement différent 4

Mapping de servlets

- En raison de l'injection de dépendances, il n'est plus possible de demander au conteneur WEB d'instancier les servlets - C'est l'injecteur qui doit s'en charger afin qu'il puisse injecter les éventuelles dépendences - Conséquence :

- Il n'y a plus de mapping de servlets dans le `web.xml`
- On demande au conteneur de router toutes les requêtes dans un objet de type  `GuiceFilter` via les lignes suivantes dans `web.xml`.
<filter>
  <filter-name>guiceFilter</filter-name>
  <filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
</filter>

<filter-mapping>
  <filter-name>guiceFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

- Le mapping est définit dans un Module de Guice (classe qui hérite de ServletModule)

public class Module extends ServletModule {
  /*Logger*/
  private static final Logger logger =
        LoggerFactory.getLogger(Module.class);

  @Override
  protected void configureServlets() {
      super.configureServlets();
      serve("/list").with(ListServlet.class);
      serve("/delete").with(DeleteServlet.class);
      logger.info("WebModule configureServlets ended.");
    }
  }

Il faut ajouter la dépendance suivante dans pom.xml en sus de la dépendance à guice et celle au logger.

<dependency>
  <groupId>com.google.inject.extensions</groupId>
  <artifactId>guice-servlet</artifactId>
  <version>5.0.1</version>
</dependency>

Les classes ListServlet et DeleteServlet doivent être des singleton bean.

Pour définir une classe comme étant un singleton bean il faut l'annoter avec l'annotation @Singleton.

Il y a une seule instance d'un singleton bean par application, cette instance est créée par le container EJB.

Les valeurs des variables d'instance du singleton bean sont conservées entre différents appels du singleton bean.

- L'injecteur est créé et configuré grâce une classe qui hérite de GuiceServletContextListener. l'injecteur est créé dès que l'application est déployée avant la moindre requête5

public class MyGuiceServletConfig extends GuiceServletContextListener {

    @Override
    protected Injector getInjector() {
    return Guice.createInjector(new Module());
  }
}

- Cette classe est spécifiée dans le web.xml :

<listener>
  <listener-class>fr.iut.MyGuiceServletConfig</listener-class>
</listener>

Exercice

Objectif : Utiliser un injecteur Guice pour instancier les servlets des exercices précédents. (remarque : les contraintes de sécurité doivent être définies dans le fichier web.xml).

  • Faites un copier-coller de votre dossier contenant les servelts créées lors de la séance.
  • Modifier une version pour utiliser Google guice.
  • Commiter et pousser.


  1. http://freemarker.org/docs/dgui_template_exp.html 

  2. http://docs.oracle.com/javaee/6/tutorial/doc/gkbaa.html 

  3. http://tomcat.apache.org/tomcat-7.0-doc/realm-howto.html 

  4. https://github.com/google/guice/wiki/ServletModule 

  5. http://tomcat.apache.org/tomcat-8.0-doc/config/listeners.html 

  6. http://fr.wikipedia.org/wiki/Remote_method_invocation_%28Java%29 

  7. http://fr.wikipedia.org/wiki/Hypertext_Markup_Language 

  8. http://fr.wikipedia.org/wiki/Representational_State_Transfer 

  9. http://fr.wikipedia.org/wiki/Servlet 

  10. http://fr.wikipedia.org/wiki/JavaServer_Pages 

  11. http://www.oracle.com/technetwork/topics/jsf-092015.html 

  12. http://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Dependency_Scope 

  13. http://freemarker.org/ 

  14. http://velocity.apache.org/engine/releases/velocity-1.4/veltag.html