Plusieurs fournisseurs d’authentification dans Spring Security

Plusieurs fournisseurs d'authentification dans Spring Security

1. Vue d'ensemble

Dans cet article rapide, nous allons nous concentrer sur l'utilisation de plusieurs mécanismes pour authentifier les utilisateurs dans Spring Security.

Nous le ferons en configurant plusieurs fournisseurs d'authentification.

2. Fournisseurs d'authentification

UnAuthenticationProvider est une abstraction pour récupérer des informations utilisateur à partir d'un référentiel spécifique (comme undatabase,LDAP,custom third party source, etc. ). Il utilise les informations utilisateur extraites pour valider les informations d'identification fournies.

En termes simples, lorsque plusieurs fournisseurs d'authentification sont définis, les fournisseurs seront interrogés dans l'ordre dans lequel ils sont déclarés.

Pour une démonstration rapide, nous allons configurer deux fournisseurs d'authentification: un fournisseur d'authentification personnalisé et un fournisseur d'authentification en mémoire.

3. Dépendances Maven

Ajoutons d'abord les dépendances Spring Security nécessaires dans notre application Web:


    org.springframework.boot
    spring-boot-starter-web


    org.springframework.boot
    spring-boot-starter-security

Et sans bottes de printemps:


    org.springframework.security
    spring-security-web
    5.0.4.RELEASE


    org.springframework.security
    spring-security-core
    5.0.4.RELEASE


    org.springframework.security
    spring-security-config
    5.0.4.RELEASE

La dernière version de ces dépendances peut être trouvée dansspring-security-web,spring-security-core etspring-security-config.

4. Fournisseur d'authentification personnalisé

Créons maintenant un fournisseur d'authentification personnalisé en implémentant l'interfaceAuthneticationProvider.

Nous allons implémenter la méthodeauthenticate - qui tente l'authentification. L'objet d'entréeAuthentication contient le nom d'utilisateur et le mot de passe fournis par l'utilisateur.

La méthodeauthenticate renvoie un objetAuthentication entièrement rempli si l'authentification est réussie. Si l'authentification échoue, elle lève une exception de typeAuthenticationException:

@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
    @Override
    public Authentication authenticate(Authentication auth)
      throws AuthenticationException {
        String username = auth.getName();
        String password = auth.getCredentials()
            .toString();

        if ("externaluser".equals(username) && "pass".equals(password)) {
            return new UsernamePasswordAuthenticationToken
              (username, password, Collections.emptyList());
        } else {
            throw new
              BadCredentialsException("External system authentication failed");
        }
    }

    @Override
    public boolean supports(Class auth) {
        return auth.equals(UsernamePasswordAuthenticationToken.class);
    }
}

Naturellement, ceci est une simple implémentation pour les besoins de notre exemple ici.

5. Configuration de plusieurs fournisseurs d'authentification

Ajoutons maintenant lesCustomAuthenticationProvider et un fournisseur d'authentification en mémoire à notre configuration Spring Security.

5.1. Configuration Java

Dans notre classe de configuration, créons et ajoutons maintenant les fournisseurs d'authentification à l'aide desAuthenticationManagerBuilder.

Tout d'abord, lesCustomAuthenticationProvider, puis un fournisseur d'authentification en mémoire en utilisantinMemoryAuthentication().

Nous nous assurons également que l'accès au modèle d'URL «/api/**» doit être authentifié:

@EnableWebSecurity
public class MultipleAuthProvidersSecurityConfig
  extends WebSecurityConfigurerAdapter {
    @Autowired
    CustomAuthenticationProvider customAuthProvider;

    @Override
    public void configure(AuthenticationManagerBuilder auth)
      throws Exception {

        auth.authenticationProvider(customAuthProvider);
        auth.inMemoryAuthentication()
            .withUser("memuser")
            .password(encoder().encode("pass"))
            .roles("USER");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.httpBasic()
            .and()
            .authorizeRequests()
            .antMatchers("/api/**")
            .authenticated();
    }


    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

5.2. Configuration XML

Alternativement, si nous voulons utiliser la configuration XML à la place de la configuration Java:


    
        
            
        
    
    



    
    

6. L'application

Ensuite, créons un point de terminaison REST simple qui est sécurisé par nos deux fournisseurs d'authentification.

Pour accéder à ce noeud final, un nom d'utilisateur et un mot de passe valides doivent être fournis. Nos fournisseurs d'authentification valideront les informations d'identification et détermineront s'ils autorisent ou non l'accès:

@RestController
public class MultipleAuthController {
    @GetMapping("/api/ping")
    public String getPing() {
        return "OK";
    }
}

7. Essai

Enfin, testons maintenant l'accès à notre application sécurisée. L'accès ne sera autorisé que si des informations d'identification valides sont fournies:

@Autowired
private TestRestTemplate restTemplate;

@Test
public void givenMemUsers_whenGetPingWithValidUser_thenOk() {
    ResponseEntity result
      = makeRestCallToGetPing("memuser", "pass");

    assertThat(result.getStatusCodeValue()).isEqualTo(200);
    assertThat(result.getBody()).isEqualTo("OK");
}

@Test
public void givenExternalUsers_whenGetPingWithValidUser_thenOK() {
    ResponseEntity result
      = makeRestCallToGetPing("externaluser", "pass");

    assertThat(result.getStatusCodeValue()).isEqualTo(200);
    assertThat(result.getBody()).isEqualTo("OK");
}

@Test
public void givenAuthProviders_whenGetPingWithNoCred_then401() {
    ResponseEntity result = makeRestCallToGetPing();

    assertThat(result.getStatusCodeValue()).isEqualTo(401);
}

@Test
public void givenAuthProviders_whenGetPingWithBadCred_then401() {
    ResponseEntity result
      = makeRestCallToGetPing("user", "bad_password");

    assertThat(result.getStatusCodeValue()).isEqualTo(401);
}

private ResponseEntity
  makeRestCallToGetPing(String username, String password) {
    return restTemplate.withBasicAuth(username, password)
      .getForEntity("/api/ping", String.class, Collections.emptyMap());
}

private ResponseEntity makeRestCallToGetPing() {
    return restTemplate
      .getForEntity("/api/ping", String.class, Collections.emptyMap());
}

8. Conclusion

Dans ce tutoriel rapide, nous avons vu comment plusieurs fournisseurs d'authentification peuvent être configurés dans Spring Security. Nous avons sécurisé une application simple à l'aide d'un fournisseur d'authentification personnalisé et d'un fournisseur d'authentification en mémoire.

Nous avons également rédigé des tests pour vérifier que l'accès à notre application nécessite des informations d'identification qui peuvent être validées par au moins l'un de nos fournisseurs d'authentification.

Comme toujours, le code source complet de l'implémentation peut être trouvéover on GitHub.