Vários provedores de autenticação no Spring Security

Vários provedores de autenticação no Spring Security

1. Visão geral

Neste artigo rápido, vamos nos concentrar no uso de vários mecanismos para autenticar usuários no Spring Security.

Faremos isso configurando vários provedores de autenticação.

2. Provedores de autenticação

UmAuthenticationProvider é uma abstração para buscar informações do usuário de um repositório específico (comodatabase,LDAP,custom third party source, etc. ). Ele usa as informações do usuário buscadas para validar as credenciais fornecidas.

Simplificando, quando vários provedores de autenticação são definidos, os provedores serão consultados na ordem em que forem declarados.

Para uma rápida demonstração, vamos configurar dois provedores de autenticação - um provedor de autenticação personalizado e um provedor de autenticação na memória.

3. Dependências do Maven

Vamos primeiro adicionar as dependências Spring Security necessárias em nosso aplicativo da web:


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


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

E, sem Spring Boot:


    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

A versão mais recente dessas dependências pode ser encontrada emspring-security-web,spring-security-core espring-security-config.

4. Provedor de autenticação personalizado

Vamos agora criar um provedor de autenticação personalizado implementando a interfaceAuthneticationProvider.

Vamos implementar o métodoauthenticate - que tenta a autenticação. O objeto de entradaAuthentication contém as credenciais de nome de usuário e senha fornecidas pelo usuário.

O métodoauthenticate retorna um objetoAuthentication totalmente preenchido se a autenticação for bem-sucedida. Se a autenticação falhar, ela lançará uma exceção do tipoAuthenticationException:

@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);
    }
}

Naturalmente, esta é uma implementação simples para o objetivo de nosso exemplo aqui.

5. Configurando vários provedores de autenticação

Vamos agora adicionar oCustomAuthenticationProvider e um provedor de autenticação na memória à nossa configuração Spring Security.

5.1. Configuração Java

Em nossa classe de configuração, vamos agora criar e adicionar os provedores de autenticação usandoAuthenticationManagerBuilder.

Primeiro, oCustomAuthenticationProvider e, em seguida, um provedor de autenticação na memória usandoinMemoryAuthentication().

Também estamos nos certificando de que o acesso ao padrão de URL “/api/**” precisa ser autenticado:

@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. Configuração XML

Como alternativa, se queremos usar a configuração XML em vez da configuração Java:


    
        
            
        
    
    



    
    

6. A aplicação

A seguir, vamos criar um endpoint REST simples que é protegido por nossos dois provedores de autenticação.

Para acessar esse terminal, é necessário fornecer um nome de usuário e senha válidos. Nossos provedores de autenticação validam as credenciais e determinam se permitem ou não o acesso:

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

7. Teste

Por fim, vamos testar o acesso ao nosso aplicativo seguro. O acesso será permitido apenas se credenciais válidas forem fornecidas:

@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. Conclusão

Neste tutorial rápido, vimos como vários provedores de autenticação podem ser configurados no Spring Security. Protegemos um aplicativo simples usando um provedor de autenticação personalizado e um provedor de autenticação na memória.

E também escrevemos testes para verificar se o acesso ao nosso aplicativo requer credenciais que podem ser validadas por pelo menos um de nossos provedores de autenticação.

Como sempre, o código-fonte completo da implementação pode ser encontradoover on GitHub.