1
resposta

Erro ao efetuar o primeiro login

Estou usando Spring Boot v2.0.0.M7 para uma aplicação web e após o registro do usuário na maioria das vezes em que efetuo login, preciso digitar duas vezes as credenciais. Em raríssimas vezes funciona na primeira execução.

Para o login estou sobrepondo o método configure do pacote org.springframework.security.config.annotation.web.builders.HttpSecurity

Segue a classe SecurityConfiguration que estende de WebSecurityConfigurerAdapter:

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled=true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    @Autowired
    DataSource dataSource;

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/login", "/registration", "/static/**").permitAll().anyRequest()
            .authenticated().and().csrf().disable()
            .formLogin().loginPage("/login").failureUrl("/login?error").defaultSuccessUrl("/app").usernameParameter("email").passwordParameter("password")
            .and().logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout")).deleteCookies("JSESSIONID").logoutSuccessUrl("/").and().exceptionHandling().accessDeniedPage("/pages/401");
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/resources/**", "/static/**", "/css/**", "/js/**", "/images/**");
    }

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

    @Bean
    public PersistentTokenRepository persistentTokenRepository() {
        JdbcTokenRepositoryImpl tokenRepositoryImpl = new JdbcTokenRepositoryImpl();
        tokenRepositoryImpl.setDataSource(dataSource);
        return tokenRepositoryImpl;
    }

}

Além da dúvida inicial, há mais uma: Para que eu possa retornar mensagens de erro para o usuário no login, como "Senha inválida" ou "Usuário não ativado", tenho que criar uma url /login que contém essa lógica ou é possível fazer isso via o método configure?

Segue pom.xml

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.0.M7</version>
</parent>
<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>3.17</version>
        </dependency>
    </dependencies>
1 resposta

Opa, em relação especificamente ao primeiro erro, não sei bem ao certo... Se fosse um login após logout, chutaria que dava problema porque o comportamento padrão do Spring Security é te levar para a última url que você acessou antes de logar... De todo jeito, você pode implementar um SuccessHandler e passar a implementação através do método successHandler na configuração do Security.

Em relação a exibir uma mensagem de erro, o jeito mais fácil é que na sua jsp você pode verificar se tem o parâmetro error e exibir a sua mensagem.

O jeito mais customizado é: Busque pela classe AbstractUserDetailsAuthenticationProvider e dentro dela referências a várias chaves buscadas no seu messages.properties pelo SpringSecurity. Basta dar um ctrl+f por "messages.get". Por exemplo, para lidar com login inválido você pode adicioanar a chave => AbstractUserDetailsAuthenticationProvider.badCredentials=Email ou senha inválido.

Na jsp você referenciar assim: ${SPRING_SECURITY_LAST_EXCEPTION.message }

Quer mergulhar em tecnologia e aprendizagem?

Receba a newsletter que o nosso CEO escreve pessoalmente, com insights do mercado de trabalho, ciência e desenvolvimento de software