Duas páginas de login com Spring Security

Duas páginas de login com Spring Security

1. Introdução

Neste tutorial, veremos como podemosconfigure Spring Security to work with two different login pages usando dois elementos Spring Securityhttp diferentes na configuração.

2. Configurando 2 Elementos Http

Uma das situações em que podemos precisar de duas páginas de logon é quando temos uma página para administradores de um aplicativo e uma página diferente para usuários normais.

Teremosconfigure two http elements que serão diferenciados pelo padrão de URL associado a cada:

  • /user * para páginas que precisarão de uma autenticação de usuário normal para serem acessadas

  • /admin * para páginas que serão acessadas por um administrador

Cada elementohttp terá uma página de login diferente e um URL de processamento de login diferente.

Para configurar dois elementoshttp diferentes, vamos criar duas classes estáticas anotadas com@Configuration que estendemWebSecurityConfigurerAdapter.

Ambos serão colocados dentro de uma classe@Configuration regular:

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    ...
}

Vamos definir oWebSecurityConfigurerAdapter para os usuários“ADMIN”:

@Configuration
@Order(1)
public static class App1ConfigurationAdapter extends WebSecurityConfigurerAdapter {
    public App1ConfigurationAdapter() {
        super();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.antMatcher("/admin*")
          .authorizeRequests()
          .anyRequest()
          .hasRole("ADMIN")

          .and()
          .formLogin()
          .loginPage("/loginAdmin")
          .loginProcessingUrl("/admin_login")
          .failureUrl("/loginAdmin?error=loginError")
          .defaultSuccessUrl("/adminPage")

          .and()
          .logout()
          .logoutUrl("/admin_logout")
          .logoutSuccessUrl("/protectedLinks")
          .deleteCookies("JSESSIONID")

          .and()
          .exceptionHandling()
          .accessDeniedPage("/403")

          .and()
          .csrf().disable();
    }
}

E agora, vamos definir oWebSecurityConfigurerAdapter para usuários normais:

@Configuration
@Order(2)
public static class App2ConfigurationAdapter extends WebSecurityConfigurerAdapter {

    public App2ConfigurationAdapter() {
        super();
    }

    protected void configure(HttpSecurity http) throws Exception {
        http.antMatcher("/user*")
          .authorizeRequests()
          .anyRequest()
          .hasRole("USER")

          .and()
          .formLogin()
          .loginPage("/loginUser")
          .loginProcessingUrl("/user_login")
          .failureUrl("/loginUser?error=loginError")
          .defaultSuccessUrl("/userPage")

          .and()
          .logout()
          .logoutUrl("/user_logout")
          .logoutSuccessUrl("/protectedLinks")
          .deleteCookies("JSESSIONID")

          .and()
          .exceptionHandling()
          .accessDeniedPage("/403")

          .and()
          .csrf().disable();
    }
}

Observe que, ao colocar a anotação@Order em cada classe estática, estamos especificando a ordem em que as duas classes serão consideradas com base na correspondência de padrões quando uma URL é solicitada.

Duas classes de configuração não podem ter a mesma ordem.

3. Páginas de login personalizadas

Criaremos nossas próprias páginas de login personalizadas para cada tipo de usuário. Para o usuário administrador, o formulário de login terá uma ação“user_login”, conforme definido na configuração:

User login page

User:
Password:

A página de login do administrador é semelhante, exceto que o formulário terá uma ação de“admin_login” conforme a configuração java.

4. Configuração de autenticação

Agora precisamosconfigure authentication for our application. Vejamos duas maneiras de fazer isso - uma usando uma fonte comum para autenticação do usuário e a outra usando duas fontes separadas.

4.1. Usando uma fonte comum de autenticação de usuário

Se ambas as páginas de login compartilham uma fonte comum para autenticação de usuários, você pode criar um único bean do tipoUserDetailsService que tratará da autenticação.

Vamos demonstrar este cenário usando umInMemoryUserDetailsManager que define dois usuários - um com uma função“USER”e um com uma função“ADMIN”:

@Bean
public UserDetailsService userDetailsService() throws Exception {
    InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
    manager.createUser(User
      .withUsername("user")
      .password(encoder().encode("userPass"))
      .roles("USER")
      .build());

    manager.createUser(User
      .withUsername("admin")
      .password(encoder().encode("adminPass"))
      .roles("ADMIN")
      .build());

    return manager;
}

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

4.2. Usando duas fontes de autenticação de usuário diferentes

Se você tiver fontes diferentes para autenticação de usuário - uma para administradores e outra para usuários normais - você pode configurar umAuthenticationManagerBuilder dentro de cada classe@Configuration estática. Vejamos um exemplo de um gerenciador de autenticação para um usuário“ADMIN”:

@Configuration
@Order(1)
public static class App1ConfigurationAdapter extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
          .withUser("admin")
          .password(encoder().encode("admin"))
          .roles("ADMIN");
    }
}

Nesse caso, o beanUserDetailsService da seção anterior não será mais usado.

6. Conclusão

Neste tutorial rápido, mostramos como implementar duas páginas de login diferentes no mesmo aplicativo Spring Security.

O código completo para este artigo pode ser encontrado emGitHub project.

Ao executar o aplicativo, você pode acessar os exemplos acima no URI/protectedLinks.