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.