Blue Flower

Chercher

II. Interfaces et classes  importantes

1. Authentication

1.1. AuthenticationManager

AuthenticationManager est l'interface principale  pour l'authentification pour une application dont la securité est gérée par  Spring Security; cette interface  peut être  considérée  comme un coordinateur qui enregistre  plusieurs fournisseurs et, en fonction du type de demande elle  envoie  la requête d'authentification au bon fournisseur; elle possède une seule méthode appelée authenticate et sa signature est :

public interface AuthenticationManager {

  Authentication authenticate (Authentication authentication)
    throws AuthenticationException;
} 

Pour Spring Security, une configuration rapide des détails des utilisateurs en mémoire, pour  JDBC , LDAP ou pour l'ajout de UserDetailsService personnalisé, on se sert de   AuthenticationManagerBuilder,  L'exemple suivant montre une application qui configure l'AuthentificationManager global (parent) :

@Configuration
public class ApplicationSecurity extends WebSecurityConfigurerAdapter {
   ... // c e qui concerne le web par exemple 
@Autowired
 public void initialize(AuthenticationManagerBuilder builder, DataSource dataSource) {   builder.jdbcAuthentication().dataSource(dataSource).withUser("dave")  .password("secret").roles("USER");
  }
}

1.2. AuthenticationProvider

L'interface AuthenticationProvider dérive de l'interface AuthentificationManager ; L'implémentation  courante de AuthenticationProvider  est la classe ProviderManager ;  AuthenticationProvider  a une méthode supplémentaire par rapport à AuthenticationProvider pour permettre à l'appelant de demander s'il prend en charge une "Authentication" type donné car  AuthenticationProvider traite des types spécifiques d’authentification;  elle possède   deux méthodes :

la méthode authenticate  effectue l'authentification avec la demande et la méthode supports  vérifie si le fournisseur prend en charge le type d'authentification indiqué. La signature de AuthenticationProvider  est :

public interface AuthenticationProvider {
	Authentication authenticate(Authentication authentication)
	    throws AuthenticationException;
	boolean supports(Class<?> authentication);
}

L'argument Class<?> dans la méthode supports() est en réalité Class<? extends Authentication> (on lui demande uniquement s'il prend en charge quelque chose qui est transmis à la méthode Authenticate()). Un objet de type ProviderManager  peut prendre en charge plusieurs mécanismes d'authentification différents dans la même application en déléguant à une chaîne de AuthenticationProviders. Si un ProviderManager ne reconnaît pas un type d’instance d’authentification particulier, ce dernier  est ignoré.
Un ProviderManager a un parent facultatif, qu'il peut consulter si tous les fournisseurs renvoient null. Si le parent n'est pas disponible, une authentification nulle entraîne une AuthenticationException.
Parfois, une application dispose de groupes logiques de ressources protégées (par exemple, toutes les ressources Web qui correspondent à un modèle de chemin, tel que /api/**), et chaque groupe peut avoir son propre AuthenticationManager dédié. Souvent, chacun d’eux est un ProviderManager et ils partagent un parent. Le parent est alors une sorte de ressource « globale », agissant comme une ressource de secours pour tous les prestataires.

Une implémentation importante de l'interface souvent utilisé dans des projets est DaoAuthenticationProvider , qui récupère les détails de l'utilisateur à partir d'une implémentation de l'interface UserDetailsService.

1.3. Interface UserdetailsService

L'interface UserDetailsService est décrit comme une interface principale qui charge des données spécifiques à l'utilisateur .

Dans la plupart des cas d'utilisation, AuthenticationProvider permet d'extraire les informations d'identité des utilisateurs en fonction des informations d'identification d'une base de données, puis effectue la validation. Ce cas d'utilisation étant si courant, les développeurs Spring ont décidé de l'extraire en tant qu'interface distincte qui expose la méthode unique "loadUserByUsername" qui accepte le nom d'utilisateur comme paramètre et renvoie l'objet d'identité de l'utilisateur.

III. Application avec Spring Security

1. Exemple pour Authentification par JWT

Après avoir discuté des éléments internes du framework Spring Security, configurons-le pour l'authentification "STATELESS" (sans état) avec un jeton JWT .
Pour personnaliser Spring Security pour l'utilisation de JWT, nous avons besoin d'une classe de configuration annotée avec @EnableWebSecurity . De plus, pour simplifier le processus de personnalisation, le framework expose une classe appelée WebSecurityConfigurerAdapter .On étend cette classe et on surcharge ses deux méthodes de manière à :
1) Configurer le gestionnaire d'authentification avec le bon fournisseur
2) Configurer la sécurité Web (URL publiques, URL privées, autorisation, etc.) ; voir ci-dessous

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // TODO configure authentication manager
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // TODO configure web security
    }

}

Dans une application classique , nous stockons les identités des utilisateurs dans une base de données. Ces identités sont mappées par l'entité User et leurs opérations CRUD sont définies par un référentiel UserRepo Spring Data.

Désormais, lorsque nous acceptons la demande d'authentification, nous devons récupérer l'identité correcte de la base de données à l'aide des informations d'identification fournies, puis la vérifier. Pour cela, nous avons besoin de l'implémentation de l'interface UserDetailsService, qui est définie comme suit :

public interface UserDetailsService {
   UserDetails loadUserByUsername (String username)
            throws UsernameNotFoundException ;
} 

Ici, nous pouvons voir qu'il est nécessaire de renvoyer l'objet qui implémente l'interface UserDetails, et notre entité User l'implémente (pour les détails d'implémentation.

Étant donné qu'il n'expose que le prototype à fonction unique, nous pouvons le traiter comme une interface fonctionnelle et fournir une implémentation sous la forme d'une expression lambda. Ici, l'appel de fonction auth.userDetailsService lancera l'instance DaoAuthenticationProvider à l'aide de notre implémentation de l'interface UserDetailsService et l'enregistrera dans le gestionnaire d'authentification. Avec le fournisseur d'authentification, nous devons configurer un gestionnaire d'authentification avec le schéma de codage de mot de passe correct qui sera utilisé pour la vérification des informations d'identification. Pour cela, nous devons exposer l'implémentation préférée de l'interface PasswordEncoder sous forme de bean. Dans notre exemple de projet, nous utiliserons l’algorithme de hachage de mot de passe bcrypt.

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter { 
   @Autowired
   private UserDetailsService userDetailsService;
 @Autowired
    private PasswordEncoder passwordEncoder;
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/admin/**").hasRole("ADMIN")
            .antMatchers("/user/**").hasRole("USER")
            .anyRequest().authenticated()
            .and()
            .formLogin()
            .and()
            .logout()
           .and()
            .csrf().disable();
    }
 @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService)
            .passwordEncoder(passwordEncoder);
   }
 }

Dans le code ci-dessus, Spring Security est activé à l'aide de l'annotation @EnableWebSecurity. La méthode configure(HttpSecurity http) est utilisée pour configurer le contrôle d'accès, en spécifiant quelles URL nécessitent quels rôles accéder et que toute demande nécessite une authentification. La méthode formLogin() active l'authentification basée sur un formulaire, la méthode logout() active la prise en charge de la déconnexion et la méthode csrf().disable() désactive la protection CSRF. La méthode configure(AuthenticationManagerBuilder auth) est utilisée pour configurer l'authentification, en spécifiant quelle implémentation UserDetailsService utiliser pour obtenir les informations utilisateur et les mots de passe, et quelle implémentation PasswordEncoder utiliser pour la vérification du mot de passe.

I. Spring Security

1. Présentation

Spring est considéré comme un terme générique qui couvre unn ensemble de framework dont Spring Security  dans l’écosystème Java.  Spring Security est un framework d'authentification et d'autorisation puissant qu'on peut  personnaliser ( customiser). Pour sécuriser des applications basées sur Spring, on l'utilise et lorsqu'on cherche à implémenter une solution de jeton Spring JWT, il est logique de la baser sur Spring Security. Malgré sa popularité, Spring Security n'est pas simple et direct à configurer. Dans Spring Security des termes son utilisés et il est important de comprendre leur signification.

1.1. L'authentification
Elle fait référence au processus de vérification de l'identité d'un utilisateur, sur la base des informations d'identification fournies. Un exemple courant consiste à saisir un nom d'utilisateur et un mot de passe lorsque vous vous connectez à un site Web. Vous pouvez y voir une réponse à la question Qui êtes-vous ? .

1.2. L'autorisation
Elle fait référence au processus permettant de déterminer si un utilisateur dispose de l'autorisation appropriée pour effectuer une action particulière ou lire des données particulières, en supposant que l'utilisateur est authentifié avec succès. Vous pouvez y voir une réponse à la question Un utilisateur peut-il faire/lire ceci ? .

1.3. Le principe
Elle fait référence à l'utilisateur actuellement authentifié.

1.4. L'autorité accordée
Elle fait référence à l'autorisation de l'utilisateur authentifié.

1.5. Le rôle
il fait référence à un groupe d'autorisations de l'utilisateur authentifié

Une fois qu'on a défini ces termes, regardons de prêt l'architecture ou plutôt comment fonctionne Spring Security. L'architecture de Spring Security est conçue de telle sorte que l'on puisse distinguer l'authentification et l'autorisation puis de pouvoir gérer des stratégies et des points d'extension pour les deux.

2. Architecture de Spring Security

Les filtres  sont des composants Java enfichables qu'on utilise pour intercepter et traiter des requêtes clientes  avant qu'elles ne soient envoyées aux  servlets; pour une application basée sur un servlet,  Le conteneur de servlet crée un objet de type FilterChain qui contient les instances  des différents filtre et de la  Servlet de l'application. 

comme Spring est un framework basé sur un servlet en l'occurence DispatcherServlet, Si on utilise Spring Security le système de filtres de Spring Security  "Spring Security Filter Chain"  est pris en charge par la chaine de filtrage du conteneur de servlets au niveau de FilterChaineProxy (voir shéma ci-dessous) par un filtre nommée DelegatingFilterProxy; celui-ci permet de faire le pont entre le cycle de vie du conteneur de servlets et ApplicationContext de Spring

 

On peut enregistrer DelegatingFilterProxy via les mécanismes du conteneur de Servlet standard, mais déléguer tout le travail à un Spring Bean qui implémente Filter.

Le client envoie une requête à l'application et le conteneur crée un filtre de type  FilterChain, qui contient les instances de filtres et le servlet qui doivent traiter les requêtes de type  HttpServletRequest, en fonction  de l'URI de la requête. Dans une application Spring MVC, le/la Servlet qui traite ces requêtes est une instance de DispatcherServlet. Tout au plus, un/une servlet peut gérer une seule requête de type  HttpServletRequest et d'une éponse de type HttpServletResponse.

Spring Security ne sécurise pas les réseaux ou les ordinateurs, mais les applications: il intervient dans le dialogue entre l'application et l'utilisateur (ou entre deux applications). Ce dialogue est géré par le servlet Spring DispatcherServlet , qui redirige les requêtes http vers les classes contrôleur de l'application. En résumé, le rôle de Spring Security est d'insérer des opérations dans cette interaction, grâce à un ensemble de filtres de servlet. Ce groupe de filtres est la chaîne de filtres de Spring Security.

3. Chaîne de filtres de Spring Security

3.1. Généralités

Comme il est précisé ci-dessus, dans une application basée sur des servlets, les requêtes en entrée passent par un certain nombre de filtres; il en est de même des filtres de Spring Security.  L'image suivante montre la superposition typique des gestionnaires de filtres pour une seule requête HTTP.

Avant de commencer à personnaliser la configuration, discutons d'abord du fonctionnement de l'authentification de Spring Security en coulisses. Le diagramme suivant montre que Spring Security est un ensemble de filtres; le traitement des requêtes en entrée est géré par "AuthenticationManager" ; le flux est ensuite dispatcher aux différents "AuthentificationProvider".

Lorsqu'on ajoute le framework Spring Security à une  application, il enregistre automatiquement une chaîne de filtres qui interceptent toutes les requêtes entrantes. Cette chaîne se compose de différents filtres, et chacun d'entre eux gère un cas d'utilisation particulier.

 Cependant, plusieurs filtres peuvent être utilisés pour  par exemple :

- Empêchez l'appel des instances de filtre en aval ou certains appels  du servlet. Dans ce cas, le filtre écrit généralement la réponse HttpServletResponse.
- Modifiez la requête de type HttpServletRequest ou la réponse de type HttpServletResponse utilisé par les instances de filtre en aval ou du  servlet.
La puissance du filtre vient de la FilterChain qui y est transmise.

La chaîne de filtrage effectue des actions avant d'atteindre le Servlet DispatcherServlet, afin de vérifier si une requête provient d'un utilisateur authentifié et autorisé, avant de la laisser aller vers les contrôleurs. En résumé La chaîne de filtrage traite  2 concepts fondamentaux :
    authentification : l'utilisateur doit être identifié par une combinaison nom d'utilisateur/mot de passe.
    autorisations : les utilisateurs ne sont pas égaux quant aux opérations qu'ils sont autorisés à effectuer. A titre d'exemple, un utilisateur qui n'est pas administrateur ne doit pas être autorisé à modifier le compte d'un autre utilisateur.

3.2. Exemples d'actions traitées par les filtre:
- Vérifiez si l'URL demandée est accessible au public, en fonction de la configuration.
- En cas d'authentification basée sur la session, vérifiez si l'utilisateur est déjà authentifié dans la session en cours.
- Vérifiez si l'utilisateur est autorisé à effectuer l'action demandée, et ainsi de suite. Un détail important qu'il ne faut pas oublier c'est que tous les  filtres Spring Security sont enregistrés avec un ordre de traitement; les ordres les plus bas sont les premiers  invoqués. Pour certains cas d'utilisation, si on souhaite placer un filtre personnalisé devant ceux de Spring Security, on doit faire la commande. Cela peut être fait avec la configuration suivante :

spring.security.filter.order=10

Une fois que nous aurons ajouté cette configuration à notre fichier " application.properties", nous aurons de l'espace pour 10 filtres personnalisés devant les filtres de Spring Security.

3.3. Exemple

......

4. Chaîne de filtres par défaut

Lors du lancement d'une application implémentant Spring Security, l'un des premiers logs apparaissant dans la console ressemble à:

2020-02-25 10:24:27.875 INFO 11116 — — [ main] o.s.s.web.DefaultSecurityFilterChain : Création d'une chaîne de filtres : toute requête […]

Ce journal répertorie les filtres par défaut implémentés par Spring Security. Ci-après, une liste ordonnée des filtres constituant la chaîne de filtres par défaut (Spring Security v5.4.2).

WebAsyncManagerIntegrationFilter : ce type est comme le ciment entre le SecurityContext et le WebAsyncManager, permettant de remplir le SecurityContext pour chaque requête.

SecurityContextPersistenceFilter : envoie les informations du SecurityContextRepository au SecurityContextHolder, ce dernier étant nécessaire au processus d'authentification, qui nécessite un SecurityContext valide.

HeaderWriterFilter : ajoute des en-têtes à la requête en cours. Ce filtre est particulièrement utile pour prévenir certaines attaques courantes en ajoutant des en-têtes comme X-Frame-Options, X-XSS-Protection et X-Content-Type-Options (voir la partie sur la protection générale ci-dessous)

CsrfFilter : ajoute une protection contre les attaques Cross-Site Request Forgery, en impliquant un jeton, qui est généralement enregistré sous forme de cookie dans la HttpSession. Il est courant d'appeler ce filtre avant toute requête pouvant changer l'état de l'application (principalement POST, PUT, DELETE et parfois OPTIONS).

LogoutFilter : gère le processus de déconnexion en appelant plusieurs lougoutHandlers chargés d'effacer le contexte de sécurité, d'invalider la session utilisateur, de rediriger vers la page par défaut…

UsernamePasswordAuthenticationFilter : analyse la soumission d'un formulaire d'authentification, qui doit fournir un nom d'utilisateur et un mot de passe. Ce filtre est activé par défaut sur l'URL /login.

DefaultLoginPageGeneratingFilter : construit une page d'authentification par défaut, à moins qu'elle ne soit explicitement inactivée. Ce filtre est la raison pour laquelle une page de connexion apparaît lors de l'implémentation de Spring Security, avant même que le développeur n'en crée une personnalisée.

DefaultLogoutPageGeneratingFilter : construit une page de déconnexion par défaut, à moins qu'elle ne soit explicitement inactivée.

BasicAuthenticationFilter : vérifie si une demande inclut un en-tête d'authentification de base et tente de se connecter avec le nom d'utilisateur et le mot de passe lus dans cet en-tête.

RequestCacheAwareFilter : vérifie dans le cache si la requête en cours est similaire à une ancienne afin d'accélérer son traitement.

SecurityContextHolderAwareRequestFilter : offre de multiples fonctionnalités pour chaque requête, notamment concernant le processus d'authentification : récupérer les informations des utilisateurs, vérifier s'ils sont authentifiés (et sinon, offrir la possibilité de s'authentifier via le gestionnaire d'authentification), obtenir leurs rôles, proposer de se déconnecter via les gestionnaires de déconnexion, maintenant le contexte de sécurité sur plusieurs threads…

AnonymousAuthenticationFilter : fournit un objet d'authentification au détenteur du contexte de sécurité s'il n'y en a pas.

SessionManagementFilter : vérifie si un utilisateur est authentifié depuis le début de la requête, et dans ce cas, procède aux vérifications liées à la session (à titre d'exemple, vérifie si plusieurs connexions simultanées sont actuellement en cours d'utilisation).

ExceptionTranslationFilter : gérez les exceptions AccessDeniedException et AuthenticationException levées par filterChain. Ce filtre est essentiel pour que l'interface graphique reste cohérente lorsque des erreurs se produisent, car il constitue le pont entre les exceptions Java et les réponses http.

FilterSecurityInterceptor : vérifie si les rôles de l'utilisateur correspondent aux exigences d'autorisation pour la demande en cours.

II. Interfaces et classes  importantes

1. Authentication

1.1. AuthenticationManager

AuthenticationManager est l'interface principale  pour l'authentification pour une application dont la securité est gérée par  Spring Security; cette interface  peut être  considérée  comme un coordinateur qui enregistre  plusieurs fournisseurs et, en fonction du type de demande elle  envoie  la requête d'authentification au bon fournisseur; elle possède une seule méthode appelée authenticate et sa signature est :

public interface AuthenticationManager {

  Authentication authenticate (Authentication authentication)
    throws AuthenticationException;
} 

Pour Spring Security, une configuration rapide des détails des utilisateurs en mémoire, pour  JDBC , LDAP ou pour l'ajout de UserDetailsService personnalisé, on se sert de   AuthenticationManagerBuilder,  L'exemple suivant montre une application qui configure l'AuthentificationManager global (parent) :

@Configuration
public class ApplicationSecurity extends WebSecurityConfigurerAdapter {
   ... // c e qui concerne le web par exemple 
@Autowired
 public void initialize(AuthenticationManagerBuilder builder, DataSource dataSource) {   builder.jdbcAuthentication().dataSource(dataSource).withUser("dave")  .password("secret").roles("USER");
  }
}

1.2. AuthenticationProvider

L'interface AuthenticationProvider dérive de l'interface AuthentificationManager ; L'implémentation  courante de AuthenticationProvider  est la classe ProviderManager ;  AuthenticationProvider  a une méthode supplémentaire par rapport à AuthenticationProvider pour permettre à l'appelant de demander s'il prend en charge une "Authentication" type donné car  AuthenticationProvider traite des types spécifiques d’authentification;  elle possède   deux méthodes :

la méthode authenticate  effectue l'authentification avec la demande et la méthode supports  vérifie si le fournisseur prend en charge le type d'authentification indiqué. La signature de AuthenticationProvider  est :

public interface AuthenticationProvider {
	Authentication authenticate(Authentication authentication)
	    throws AuthenticationException;
	boolean supports(Class<?> authentication);
}

L'argument Class<?> dans la méthode supports() est en réalité Class<? extends Authentication> (on lui demande uniquement s'il prend en charge quelque chose qui est transmis à la méthode Authenticate()). Un objet de type ProviderManager  peut prendre en charge plusieurs mécanismes d'authentification différents dans la même application en déléguant à une chaîne de AuthenticationProviders. Si un ProviderManager ne reconnaît pas un type d’instance d’authentification particulier, ce dernier  est ignoré.
Un ProviderManager a un parent facultatif, qu'il peut consulter si tous les fournisseurs renvoient null. Si le parent n'est pas disponible, une authentification nulle entraîne une AuthenticationException.
Parfois, une application dispose de groupes logiques de ressources protégées (par exemple, toutes les ressources Web qui correspondent à un modèle de chemin, tel que /api/**), et chaque groupe peut avoir son propre AuthenticationManager dédié. Souvent, chacun d’eux est un ProviderManager et ils partagent un parent. Le parent est alors une sorte de ressource « globale », agissant comme une ressource de secours pour tous les prestataires.

Une implémentation importante de l'interface souvent utilisé dans des projets est DaoAuthenticationProvider , qui récupère les détails de l'utilisateur à partir d'une implémentation de l'interface UserDetailsService.

1.3. Interface UserdetailsService

L'interface UserDetailsService est décrit comme une interface principale qui charge des données spécifiques à l'utilisateur .

Dans la plupart des cas d'utilisation, AuthenticationProvider permet d'extraire les informations d'identité des utilisateurs en fonction des informations d'identification d'une base de données, puis effectue la validation. Ce cas d'utilisation étant si courant, les développeurs Spring ont décidé de l'extraire en tant qu'interface distincte qui expose la méthode unique "loadUserByUsername" qui accepte le nom d'utilisateur comme paramètre et renvoie l'objet d'identité de l'utilisateur.

III. Application avec Spring Security

1. Exemple pour Authentification par JWT

Après avoir discuté des éléments internes du framework Spring Security, configurons-le pour l'authentification "STATELESS" (sans état) avec un jeton JWT .
Pour personnaliser Spring Security pour l'utilisation de JWT, nous avons besoin d'une classe de configuration annotée avec @EnableWebSecurity . De plus, pour simplifier le processus de personnalisation, le framework expose une classe appelée WebSecurityConfigurerAdapter .On étend cette classe et on surcharge ses deux méthodes de manière à :
1) Configurer le gestionnaire d'authentification avec le bon fournisseur
2) Configurer la sécurité Web (URL publiques, URL privées, autorisation, etc.) ; voir ci-dessous

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // TODO configure authentication manager
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // TODO configure web security
    }

}

Dans une application classique , nous stockons les identités des utilisateurs dans une base de données. Ces identités sont mappées par l'entité User et leurs opérations CRUD sont définies par un référentiel UserRepo Spring Data.

Désormais, lorsque nous acceptons la demande d'authentification, nous devons récupérer l'identité correcte de la base de données à l'aide des informations d'identification fournies, puis la vérifier. Pour cela, nous avons besoin de l'implémentation de l'interface UserDetailsService, qui est définie comme suit :

public interface UserDetailsService {
   UserDetails loadUserByUsername (String username)
            throws UsernameNotFoundException ;
} 

Ici, nous pouvons voir qu'il est nécessaire de renvoyer l'objet qui implémente l'interface UserDetails, et notre entité User l'implémente (pour les détails d'implémentation.

Étant donné qu'il n'expose que le prototype à fonction unique, nous pouvons le traiter comme une interface fonctionnelle et fournir une implémentation sous la forme d'une expression lambda. Ici, l'appel de fonction auth.userDetailsService lancera l'instance DaoAuthenticationProvider à l'aide de notre implémentation de l'interface UserDetailsService et l'enregistrera dans le gestionnaire d'authentification. Avec le fournisseur d'authentification, nous devons configurer un gestionnaire d'authentification avec le schéma de codage de mot de passe correct qui sera utilisé pour la vérification des informations d'identification. Pour cela, nous devons exposer l'implémentation préférée de l'interface PasswordEncoder sous forme de bean. Dans notre exemple de projet, nous utiliserons l’algorithme de hachage de mot de passe bcrypt.

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter { 
   @Autowired
   private UserDetailsService userDetailsService;
 @Autowired
    private PasswordEncoder passwordEncoder;
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/admin/**").hasRole("ADMIN")
            .antMatchers("/user/**").hasRole("USER")
            .anyRequest().authenticated()
            .and()
            .formLogin()
            .and()
            .logout()
           .and()
            .csrf().disable();
    }
 @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService)
            .passwordEncoder(passwordEncoder);
   }
 }

Dans le code ci-dessus, Spring Security est activé à l'aide de l'annotation @EnableWebSecurity. La méthode configure(HttpSecurity http) est utilisée pour configurer le contrôle d'accès, en spécifiant quelles URL nécessitent quels rôles accéder et que toute demande nécessite une authentification. La méthode formLogin() active l'authentification basée sur un formulaire, la méthode logout() active la prise en charge de la déconnexion et la méthode csrf().disable() désactive la protection CSRF. La méthode configure(AuthenticationManagerBuilder auth) est utilisée pour configurer l'authentification, en spécifiant quelle implémentation UserDetailsService utiliser pour obtenir les informations utilisateur et les mots de passe, et quelle implémentation PasswordEncoder utiliser pour la vérification du mot de passe.

II.  L'architecture du Framework Spring

1. Présentation

Le framework Spring comprend de nombreux modules tels que core, beans, contexte, langage d'expression, AOP, Aspects, Instrumentation, JDBC, ORM, OXM, JMS, Transaction, Web, Servlet, Struts, etc...,  comme indiqué dans le diagramme suivant.

1.2. Core container

Le Core Container est composé des modules  Core proprement dit,  de Beans, de Context  et de EL (Expression Language) : 

  • Le module Core fournit les fonctionnalités fondamentales du framework en plus de  l'IoC et l'injection de Dependance.
  • Le module Beans quand à  lui fournit l'interface BeanFactory qui permet une implémentation du pattern Factory .
  • Le module Context s'appuie sur la base solide fournie par les modules Core et Beans et constitue un moyen permettant d'accéder aux objets définis et configurés au sein d'une application développée avec Spring. L'interface ApplicationContext est le point focal du module Context.
  • Le module SpEL (Spring Expression Langage) fournit un langage d'expression (dérivant de EL)  puissant qui permet   manipuler des d'objets à l'exécution d'une application Spring.

1.3. Data Access/Integration

 La couche Access/Integration est composée de JMS et du module de Transaction :

  • Le module JDBC fournit une couche d'abstraction JDBC qui supprime le besoin d'un codage fastidieux lié à JDBC.
  • Le module ORM fournit des couches d'intégration pour les API de mappage objet-relationnelles les plus répandues, notamment JPA, JDO, Hibernate et iBatis.
  • Le module OXM fournit une couche d'abstraction prenant en charge les implémentations de mappage Object / XML pour JAXB, Castor, XMLBeans, JiBX et XStream.
  • Le module Java Messaging Service JMS contient des fonctionnalités permettant de générer et de consommer des messages.
  • Le module Transaction prend en charge la gestion des transactions programmatiques et déclaratives pour les classes qui implémentent des interfaces spéciales et pour tous les POJO.

1.4. Web

La couche Web comprend les modules Web, Web-MVC, Web-Socket et Web-Portlet dont les détails sont les suivants:

  • Le module Web fournit des fonctionnalités d'intégration Web de base, telles que la fonctionnalité de téléchargement de fichiers en plusieurs parties et l'initialisation du conteneur IoC à l'aide des servlets Listner et Context orienté application Web.
  • Le module Web-MVC contient l'implémentation Model-View-Controller (MVC) de Spring pour les applications Web.
  • Le module Web-Socket prend en charge la communication bidirectionnelle basée sur WebSocket entre le client et le serveur dans les applications Web.
  • Le module Web-Portlet fournit l'implémentation MVC à utiliser dans un environnement de portlet et reflète la fonctionnalité du module Web-Servlet.

 ...

précédent

III. Autres modules

Il y a quelques autres modules importants comme les modules AOP, Aspects, Instrumentation, Web et Test:

  • Le module AOP fournit une implémentation de programmation orientée aspect vous permettant de définir des intercepteurs de méthode et des Pointcuts pour découper proprement le code qui implémente des fonctionnalités à séparer .
  • Le module Aspects fournit l'intégration avec AspectJ, qui est à nouveau un framework AOP puissant et mature .
  • Le module Instrumentation fournit un support d'instrumentation de classe et des implémentations de chargeur de classes à utiliser dans certains serveurs d'applications .
  • Le module Messaging (module de messagerie) prend en charge STOMP en tant que sous-protocole WebSocket à utiliser dans les applications. Il prend également en charge un modèle de programmation d'annotation pour le routage et le traitement des messages STOMP à partir des clients WebSocket .
  • Le module  Test (module de teste) prend en charge les tests des composants Spring avec les frameworks JUnit ou TestNG .

 ...

précédent

I.  Le Framework Spring MVC

1. Présentation

1.1. Généralités

 Spring MVC est un module du  framework Spring; son architecture obéit au pattern  MVC (Model-View-Controller) et  il permet de développer en java des applications Web flexibles et faiblement couplées. Avec le modèle MVC on sépare les 3   parties d'une application web en java à savoir le traitement  des requêtes d'entrée envoyées par le client, la logique métier et la logique UI (affichage des résultats en réponses aux requêtes) .

  • La partie Modèle encapsule les données de l'application; ces données sont en général définies et traitées par des simples  POJO (simple classe java).
  • La partie Vue est responsable du rendu des données du modèle;  en général, elle génère une sortie HTML que le navigateur du client peut interpréter.
  • La partie Contrôleur est responsable du traitement des demandes des utilisateurs et de la construction d'un modèle de vue approprié pour le rendu du résultat

Dans  Spring-MVC le container (conteneur)  gère :

  • Le contexte de l’application Web,
  • Les objets traitant les requêtes (Controller),
  • Les objets créant les pages HTML (View),
  • Les objets données des formulaires (les requêtes),
  • Les liens avec les couches métiers et la Base de Données,
  • Le mapping des URL vers les contrôleurs,
  • Le mapping des vues, etc.

Le fait que dans Spring MVC on ait un couplage moins fort (couplage lazy) entre les différentes classes est dû au concept de programmation appelé IoC (Inversio of Control); celui-ci offre un  mécanisme qui facilite la mise en place des dépendances entre les classes par l'injection automatique des objets. Sous Spring,les objets traités sont  des beans et l' injection automatique des objets se fait soit en se servant de fichiers XML, soit par des annotations, soit  par les constructeurs ou les Setters des beans.
L'inversion de contrôle  laisse au conteneur en l'occurrence ici celui de Spring la responsabilité de gérer le cycle de vie des objets. Avec l'IoC on  référence des interfaces ou des classes plus génériques ce qui permet d'avoir un code clair, réutilisable et facile à tester. L’inversion de contrôle permet de changer le comportement de l’application, en modifiant la description xml du conteneur, sans changer les éléments programmés!
Sans IoC  la création d'objets se ferait par l'opérateur  new et cette création d'objets par instanciation  augmente les dépendances (couplage fort) entre classes.

1.2. Le modèle

Dans le contexte de Spring MVC, un modèle représente généralement les données qui seront transmises vers la vue depuis une opération (définie dans un contrôleur Web) . Spring MVC prend en charge diverses options et modèles pour définir les données pouvant être échangées. Une application Spring MVC typique peut utiliser une combinaison de ces options. La flexibilité de Spring MVC permettant d’avoir un contrôle fin pour la définition des modèles est très puissante, mais elle s'accompagne d’une variabilité et d’une complexité considérables dans la mise en oeuvre..

Une méthode d'un contrôleur dans Spring peut prendre en charge ou pas des paramètres d'entrée, et les principaux mécanismes de Spring permettant de spécifier ces paramètres d'entrée sont des annotations comme @RequestParam et @ModelAttribute.
L'annotation @RequestParam est utilisée pour lier des paramètres de requêtes individuels, tels que des chaînes et des entiers, aux paramètres d'une méthode du contrôleur concerné. L'annotation @ModelAttribute est utilisée pour lier des objets complexes, tels que des objets de domaine, des objets de transfert de données et / ou des objets de sauvegarde de formulaire, aux paramètres de méthodes d'un contrôleur.

Une méthode d'un contrôleur dans Spring peut également retourner des données issues d'un modèle. Le principal mécanisme utilisé dans Spring pour spécifier les données de modèle de sortie est l'objet ModelAndView. Si la méthode ne renvoie pas des données, la méthode du contrôleur peut simplement renvoyer une chaîne représentant la vue à restituer. Si la méthode du contrôleur renvoie des données, un objet ModalAndView doit être instancié et chaque variable de sortie est ajoutée en tant qu'attribut de modèle à l'objet ModelAndView.

1.3. La vue

Dans  Spring MVC, une vue génère l'interface utilisateur en fonction des données issues du modèle. Il existe diverses technologies basées sur Java pour l'implémentation des vues, mais Java ServerPages (JSP) est la technologie prédominante pour la définition des vues. Les JSP sont conçus pour être implémentés de manière optimisée pour les concepteurs Web.

...

1.4. Le contrôleur

Le Front contrôleur  traite les requêtes provenant de l'extérieur :

  1. Il analyse l'URL de la requête,
  2. Il appelle le contrôleur qui doit traiter la requête; ce dernier renvoie un objet de type Modèle et le nom logique de la page qui sert à construire la page à afficher,
  3. Il appelle la page demandée et celle-ci  construit la réponse
  4. Il renvoie la réponse au client demandeur.

Le contrôleur du framework Spring MVC, la servlet DispatcherServlet gère donc toutes les requêtes et réponses HTTP de toutes les applications développées avec Spring MVC. Le workflow de traitement des requêtes de Spring  MVC  est illustré dans le diagramme présenté à coté. la servlet DispatcherServlet est en faite le tour de contrôle  de Spring MVC, précisément c'est le Contrôleur dans le Design Pattern MVC. Chaque requête Web  traitée par Spring MVC passe par cette servlet DispatcherServlet. C'est une implémentation du Pattern  Front Controller; il fournit un point d’entrée unique dans toute  application Spring MVC. C'est aussi le pont entre Java et Spring. La servlet  DispatcherServlet est, comme tout autre Servlet d'une application web Java EE, déclarée dans le fichier web.xml avec un modèle d’URL, mais la seule particularité est que le modèle d’URL pour la servlet de répartition est suffisant pour mapper chaque requête Web à DispatcherServlert.

En résumé on a la servlet org.springframework.web.servlet.DispatcherServlet qui est le point d’entrée générique qui délègue les requêtes à des Controller et en principe à un org.springframework.web.servlet.mvc.Controller qui prend en charge une requête,  utilise la couche métier pour répondre en fabriquant  un modèle sous la forme d’une java.util.Map qui contient les éléments de la réponse. C'est le Controller qui choisit une org.springframework.web.servlet.View qui sera paramétrée par la Map pour donner la page qui sera affichée.

On doit mapper les requêtes  que le DispatcherServlet gère, en précisant chaque URL dans le fichier WEB-INF/web.xml. Ci-dessous on a un fichier web.xml au sein duquel on montre  la déclaration de DispatcherServlet ainsi que le mappage ; dans notre exemple  dispatcher est le nom d'une instance de  DispatcherServlet  dans notre application.

Dans Servlet 2.4 (novembre 2003), un conteneur de servlet (tel que Tomcat et Jetty) démarrait simplement l'application Web en recherchant le fichier WEB-INF/web.xml (le descripteur de déploiement). Le fichier WEB-INF/web.xml contient des références aux servlets, filtres (filters) et listner, avec leurs modèles et paramètres d'URL associés. En utilisant web.xml, le conteneur de servlet sait exactement où trouver tout et comment les configurer.

Depuis Servlet 3.0 (décembre 2009), le fichier WEB-INF/web.xml est facultatif et  on peut également utiliser des annotations ou une configuration par programmation à la place.

 

II. La  configuration dans Spring

1. Présentation

1.1.  La configuration par fichiers XML

La configuration de Spring se fait soit par programmation  soit  par fichiers XML. Dans la configuration par fichiers XML les classes représentées sont des beans. Si on utilise les annotations, on peut réduire et même éviter certains des  fichiers XML de configuration. Cependant, les fichiers de configuration XML sont très populaires et faciles à utiliser. Les principaux fichiers de configuration de Spring MVC son précisés dan  web.xml le descripteur de déploiement ( chaque application java web en possède un ) .

1.1.1. le fichier web.xml

Le fichier descripteur de dépoiement "WEB-INF/web.xml", défini  dans le répertoire ".../WEB-INF, contient le point d'entrée de l'application à savoir la servlet  DispatcherServlet le contrôleur qui traite les requêtes en entrée. Au  lancement, Spring initialise le contrôleur  DispatcherServlet, puis charge le contexte de l’application à partir du fichier  [nom_servlet]-servlet.xml situé lui aussi dans le répertoire ".../WEB-INF" (Voir la valeur de la partie  entre-crochet ([nom_servlet]) dans  la balise <param-value>  sous la la balise <init-param> dans  web.xml).

La servlet DispatcherServlet initialise donc un objet "org.springframework.web.context.WebApplicationContext" (qui dérive de ApplicationContext) dont une des implémentations   donne l'objet "org.springframework.web.context.support.XmlWebApplicationContext". Cet objet XmlWebApplicationContext contient la configuration de tous les beans de l'application qui seront définis  dans le conteneur Spring. Cet objet utilisera le fichier de configuration Spring  [nom-servlet]-servlet.xml.

on peut définir plusieurs Servlets dans "web.xml". Chaque servlet peut être initialisée, mappée à différentes URL;   certaines définitions de beans peuvent être répétées dans de nombreux conteneurs Spring; org.springframework.web.context.ContextLoaderListener est créée pour traiter ce problème; l'objet  org.springframework.web.context.WebApplicationContext définit le contexte racine de  toute l'application web; ce contexte racine est partagé par toutes les servlets de l'application . ContextLoaderListener utilisera le nom du fichier défini dans la balise <param-value> à l'intérieur de de la balise <context-param> comme nom du contexte racine de l'application.

Dans le fichier root-context.xml, nous définirons le bean, les propriétés partagées entre les conteneurs Spring, chaque conteneur Spring dans chaque servlet  utilisera ce bean, ses propriétés et les attributs définis pour lui-même;

Remarque:
A partir de Spring .3.0 on définit le fichier "[nom_servlet]-servlet.xml" dans le  répertoire  ".../WEB-INF/spring" et le fichier web.xml est devenu optionnel.

1.1.2. Exemple de fichier web.xml

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    id="WebApp_ID" version="3.0" > <!-- The definition of the Root Spring Container shared by all Servlets and Filters --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/root-context.xml </param-value>
</context-param>  
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
       <param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/dispatcher-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>*</url-pattern>
</servlet-mapping>
.......
</web-app>

1.1.3. Détail du fichier web.xml

  1. Balise "<context-param>"
    Cette balise au sein de "web.xml" sert à définir le contexte général de l'application i.e "ApplicationContext"; en effet lorsque on aura une application web qui nécessitera plusieurs "[nom_servlet]-servlet.xml"  donc plusieurs servlets, on y déclarera les différents beans qui sont utilisées  dans le fichier de configuration dont le nom est sous la balise <param-value> de la balise  <context-param>.
  2. Balise "<listener>"
    Un listener de contexte est appelé au déploiement de l’application pour charger un contexte d’application Spring. La localisation de ce contexte est fournie par le paramètre contextConfigLocation, également déclaré dans le fichier web.xml. Ce contexte d’application Spring contient les beans de l’application; c'est la balise mère qui englobe les balises de la classe listener  
  3. Balise "<listener-class>"
    on y définit la cla calsse listener à savoir "ContextLoaderListener";
  4. La balise <servlet>
    Sous la balise "<servlet>" on a déclaré une balise "<servlet-name>" qui définit le nom d'une instance de la servelt DispatcherServlet en l'occurrence ce nom est "dispatcher" ici dans notre exemple; cette instance sera initialisée avec un paramètre nommé "contextConfigLocation" qui contient le chemin d'accès au fichier  de configuration  "dispatcher-servlet.xml".
    On a aussi sous  cette balise "<servlet>" la balise "<load-on-startup>" qui est une valeur  entière qui spécifie l'ordre de chargement si jamais on a dans web.xml déclaré  plusieurs servlets. Ainsi, si vous devez déclarer plusieurs servlets, vous pouvez définir dans quel ordre elles seront initialisées. Les servlets marquées avec des entiers inférieurs sont chargés avant les servlets marquées avec des entiers plus élevés.
  5. Balise  <servlet-class>
    nom de la classe
  6. Balise  <servlet-mapping>
    Avec cette balise "<servlet-mapping>", nous le lions la servelt par son nom à un modèle d’URL qui spécifie les requêtes HTTP qu’il traitera.
  7. Balise  <url-pattern>
    ....
  8. Balise  <servlet-name>
    Sous la balise <servlet-name> est défini le nom d'une instance de la servlet DispatcherServlet
  9. Balise <load-on-startup>
    Sous cette balise est définie  une valeur  entière qui spécifie l'ordre de chargement des servlets si jamais on a dans web.xml déclaré  plusieurs servlets

..

...

...

 

1.1.4. WebApplicationContext

Dans toute application Java EE on dispose d'une interface ApplicationContext qui fournit toutes les informations de configuration. Le framework Spring  propose plusieurs classes qui implémentent cette interface  représentant le contexte racine de l'application; tous les différents éléments de configuration de l'application sont définis dans  le fichier de configuration applicationContext.xml qui est chargé par le ContextLoaderListener (voir dans le  fichier WEB-INF/web.xml le nom de notre fichier de configuration ici qui est root-context.xml ).

Pour une application web, le contexte est spécifié par l'interface WebApplicationContext qui étend ApplicationContext et qui ajoute une méthode de récupération de l'API Servlet standard ServletContext pour l'application Web. Outre les scopes standards (portées standards)  singleton et prototype  des beans Spring, trois scopes supplémentaires sont disponibles dans un contexte d'application Web:

  • request - étend une définition de bean unique au cycle de vie d'une requête HTTP unique; c'est-à-dire que chaque requête HTTP a sa propre instance d'un bean créé à l'arrière d'une définition de bean unique.
  • session - étend une définition de bean unique au cycle de vie d'une session HTTP.
  • application - étend une définition de bean unique au cycle de vie d'un ServletContext.

......

1.1.5. Le fichier [nom_servlet]-servlet.xml

Nous avons vu plus haut que spring charge springMVC-servlet.xml ([nom_de_la _servlet]-servlet.xml) par défaut à partir du répertoire WEB-INF. On peut remplacer ce comportement par défaut en se servant du paramètre contextConfigLocation comme paramètre d'initialisation (voir dans la balise   <init-param>  de web.xml) de DispatcherServlet. Dans ce qui suit, nous pouvons charger dispatcher-servlet.xml au lieu de springMVC-servlet.xml en donnant comme valeur de la balise  <init-param> la valeur "dispatcher".

1.1.6.Détail du fichier  [nom_servlet]-servlet.xml

le fichier [nom_servlet]-servlet.xml s'appelle fichier de contexte pour l'application. Dans une application on peut avoir plusieurs fichiers de contexte; ce fichier  a pour rôle l'analyse des composants de l'application, des annotations Web Spring (@Controller)  mais aussi il configure  le modèle. Le framework Spring charge le contexte de l'application à partir de ce fichier [nom_servlet]-servlet.xml.

  1. La balise <context: composant-scan  ...>
    permet d'activer l'analyse des annotations Spring MVC,
  2. La balise <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver> est utilisé pour définir les règles permettant de résoudre les noms de vue.
  3. ....
  4. ....

2.1.  La configuration par programmation

Depuis la spécification Servlet 3.0, on peut développer une  application Spring  sans utiliser des fichiers de configuration "xml". Comme toutes les  servlets, la servlet org.springframework.web.servlet.DispatcherServlet nécessite une configuration  pour que le conteneur Web puisse l'amorcer et le mapper afin qu'elle puisse traiter les requêtes;  cette  configuration de DispatcherServlet est un processus bidirectionnel. Tout d'abord, on doit préciser  au conteneur de charger la servlet et de la   mapper à un ou plusieurs Urlpatterns. Après l'amorçage, la  servlet utilise le contexte de l'application (org.springframework.web.context.WebApplicationContext ) pour se configurer. Ainsi la servlet va  détecter les composants nécessaires à partir de ce contexte de l'application et si elle ne le trouve pas, elle  utilisera les valeurs  par défaut (dans la plupart des cas). Toute ces différentes étapes vont se faire par programmation en commençant par l'amorçage DispatcherServlet.

... 

2.1. Amorcer  DispatcherServlet

 La spécification Servlet 3.0 a introduit plusieurs options pour configurer et enregistrer une servlet:

  1. utilisation d'un fichier web.xml (devenu optionnel).
  2. utilisation d'un web-fragment.xml.
  3. utilisation de javax.servlet.ServletContainerInitializer.
  4. On peut obtenir une quatrième option en implémentant  l'interface org.springframework.web.WebApplicationInitializer

Ici c'est la troisième option qui nous intéresse i.e l'utilisation de  "javax.servlet.ServletContainerInitializer". La servlet DispatcherServlet a besoin de WebApplicationContext qui doit contenir tous les beans concernées par la configuration; toutefois, par défaut, DispatcherServlet crééra un "org.springframework.web.context.support.XmlWebApplicationContext" et de ce faite  Toute application Spring est à même de charger la servlet "org.springframework.web.servlet.DispatcherServlet" et le mapper à toutes les requêtes arrivant sur "/ *".

La configuration de servlet par programmation est assurée par l'interface ServletContainerInitializer et Spring l'implémente  via la classe SpringServletContainerInitializer qui gère d'autres interfaces comme WebApplicationInitializer qui  permet la configuration du contexte (ServletContext ) par programmation.
La classe AbstractAnnotationConfigDispatcherServletInitializer implémente WebMvcConfigurer qui, à son tour implémente en interne WebApplicationInitializer, enregistre un ContextLoaderlistener (optionnelle) et un DispatcherServlet ce qui permet d'ajouter facilement des classes de configuration à charger pour les deux classes, d'appliquer des filtres au DispatcherServlet et de fournir le mappage de servlets.

Remarque:
Dans une configuration type, le contexte racine est chargé par ContextLoaderListener et le contexte de servlet par DispatcherServlet.

2.1.1. exemple de configuration par programmation

La classe AppInitializer
Cette classe étend la classe abstraite AbstractAnnotationConfigDispatcherServletInitializer et on a vu que celle-ci gère "WebMvcConfigurer" et "WebApplicationInitializer" donc elle va amorcer DispatcherServlet et permettre de réaliser les différentes configurations attendues.

package com.omara.springdataJPA.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
  @Override
  protected Class<?>[] getRootConfigClasses() {	
    return new Class[] {
	PersistenceJPAConfig.class
   };
 }
 @Override
 protected Class<?>[] getServletConfigClasses() {
  // TODO Auto-generated method stub
 return new Class[] {
  WebMvcConfig.class
  };
}
 @Override
 protected String[] getServletMappings() {
  // TODO Auto-generated method stub
  return new String[] {
   "/"		
 };
}

}

2.1.2. Détails

Dans cette classe  on implémente trois méthodes à savoir getRootConfigClasses(), getServletMappings() et getServletConfigClasses():

1. getRootConfigClasses()
Elle retourne "PersistenceJPAConfig.class"; cet objet va permettre de définir et de configurer la base de donnée de l'application (Datasource), de préciser l'implémentation à utiliser pour la specification JPA ( HibernateJpaVendorAdapter ) et les propriétés additionnelles dont on a besoin ( voir objet Properties):
PersistenceJPAConfig.java

package com.omara.springmvc.config;

import java.util.Properties;

import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@EnableTransactionManagement
@PropertySource({
    "classpath:database.properties"
})
@ComponentScan({
    "com.omara.springmvc"
})
@EnableJpaRepositories(basePackages = "com.omara.springmvc.repository")
public class PersistenceJPAConfig {

    @Autowired
    private Environment env;

    public PersistenceJPAConfig() {
        super();
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
        final LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
        entityManagerFactoryBean.setDataSource(dataSource());
        entityManagerFactoryBean.setPackagesToScan(new String[] {
            "com.omara.springmvc.entity"
        });

        final HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        entityManagerFactoryBean.setJpaVendorAdapter(vendorAdapter);
        entityManagerFactoryBean.setJpaProperties(additionalProperties());

        return entityManagerFactoryBean;
    }

    final Properties additionalProperties() {
        final Properties hibernateProperties = new Properties();
        hibernateProperties.setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
        hibernateProperties.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect"));
        hibernateProperties.setProperty("hibernate.cache.use_second_level_cache", env.getProperty("hibernate.cache.use_second_level_cache"));
        hibernateProperties.setProperty("hibernate.cache.use_query_cache", env.getProperty("hibernate.cache.use_query_cache"));
        // hibernateProperties.setProperty("hibernate.globally_quoted_identifiers", "true");
        return hibernateProperties;
    }

    @Bean
    public DataSource dataSource() {
        final DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName"));
        dataSource.setUrl(env.getProperty("jdbc.url"));
        dataSource.setUsername(env.getProperty("jdbc.user"));
        dataSource.setPassword(env.getProperty("jdbc.pass"));
        return dataSource;
    }

    @Bean
    public PlatformTransactionManager transactionManager(final EntityManagerFactory emf) {
        final JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(emf);
        return transactionManager;
    }

    @Bean
    public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
        return new PersistenceExceptionTranslationPostProcessor();
    }
}

...

Dans une application Spring type, il existe deux objets qui représentent deux instances de contexte pour l'application; l'une est ApplicationContext,  le contexte de la racine de l'application et la seconde  est le contexte de l'application en tant que servlet. Le contexte de la racine de l'application  contient généralement des ressources partagées/générales comme DataSource, des services, des référentiels, etc. Le contexte de l'application en tant que servlet contient des beans spécifiques à ce contexte, généralement des choses comme le résolveur de vues, les mappages de gestionnaires, les contrôleurs, etc. Le contexte de servlet utilise le contexte racine comme un parent et peut ainsi voir les beans définis là-dedans (la racine ne connaît pas les contextes de servlet!).
Ainsi, getRootConfigClasses () configurera ContextLoaderListener -  c' est  facultatif (on peut retourner null ou un tableau vide).

2. GetServletConfigClasses ()
Elle configure le DispatcherServlet (et est requis).
....

3. getServletConfigClasses()
...

...... 

L'interface WebMvcConfigurer

La classe WebMvcConfig ci-dessous implément l'interface WebMvcConfigurer; cette dernière

package com.omara.springmvc.config;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;

/**
 * @author Aly OMARA
 */

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = { "com.omara.springDataJPA"})
public class WebMvcConfig implements WebMvcConfigurer {
   @Bean
   public InternalResourceViewResolver resolver() {
      InternalResourceViewResolver resolver = new InternalResourceViewResolver();
      resolver.setViewClass(JstlView.class);
      resolver.setPrefix("/WEB-INF/views/");
      resolver.setSuffix(".jsp");
      return resolver;
   }
  @Override
  public void addResourceHandlers(ResourceHandlerRegistry registry) {
  registry
 .addResourceHandler("/resources/**")
 .addResourceLocations("/resources/");
 }
   @Bean
   public MessageSource messageSource() {
      ResourceBundleMessageSource source = new ResourceBundleMessageSource();
      source.setBasename("messages");
      return source;
   }
 
   @Override
   public Validator getValidator() {
      LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
      validator.setValidationMessageSource(messageSource());
      return validator;
   }
}

....

  • WebMvcConfigurer définit des options pour la personnalisation ou l'ajout à la configuration Spring MVC par défaut activée via @EnableWebMvc.    
  •  @EnableWebMvc active la configuration Spring MVC par défaut et enregistre les composants d'infrastructure Spring MVC attendus par DispatcherServlet.     
  • @Configuration indique qu'une classe déclare une ou plusieurs méthodes @Bean et peut être traitée par le conteneur Spring pour générer des définitions de bean et des demandes de service pour ces beans lors de l'exécution.   
  •  L'annotation @ComponentScan est utilisée pour spécifier les packages de base à analyser. Toute classe annotée avec @Component et @Configuration sera analysée.  
  •  InternalResourceViewResolver aide à mapper les noms des vues logiques pour afficher directement les fichiers dans un certain répertoire préconfiguré.     
  • ResourceBundleMessageSource accède aux ensembles de ressources à l'aide de noms de base spécifiés (ici, il s'agit de messages).    
  •  LocalValidatorFactoryBean démarre javax.validation.ValidationFactory et l'expose via l'interface Spring Validator, l'interface JSR-303 Validator et l'interface ValidatorFactory elle-même.  

3. Les interfaces BeanFactory et ApplicationContext
L'interface BeanFactory (le container et l'interface porte le même nom) fournit un mécanisme avancé de configuration des beans managés, utilisant potentiellement n'importe quel type de stockage.
L'interface ApplicationContext (le container et l'interface porte le même nom) est construite au dessus de la classe BeanFactory; il y  ajoute d'autres dispositifs comme l'intégration plus facile avec l'AOP, la manipulation des messages (pour l'internationalisation), la propagation des événements, les mécanismes déclaratifs pour créer l'ApplicationContext et les contextes parents optionnels,  et des contextes spécifiques aux couches des applications comme WebApplicationContext qui dérive de l'interface ApplicationContext et qui est spécifique aux applications Web.

...

(à suivre)...

précédent