Pular para o conteúdo principal

Autenticação com Spring Boot, Spring Security e AngularJS - Parte 1

Hoje iremos analisar o processo de autenticação em uma aplicação que utiliza SpringBoot, SpringSecurity e AngularJS.

Conhecendo o Spring Security


Em toda aplicação o processo de autenticação é extremamente importante, sendo um dos requisitos não funcionais mais importantes em 80% das aplicações.

Para executar tal tarefa , podemos criar rotinas ou usar frameworks já preparados para os mais diferentes cenários, no caso, vamos analisar o Spring Security, este faz parte dos componentes da plataforma Spring.

Um ponto bem interessante do Spring Security, é que ele pode ser usando em projetos que usam ou não o Spring em sua essência, por exemplo, um projeto feito somente com JEE, pode usar o Spring Security para gerenciar a parte de segurança normalmente, para mais detalhes podemos acessar a documentação oficial:


Integrando o SpringSecurity com SpringBoot


Para utilizar o SpringSecurity em uma aplicação SpringBoot, temos que adicionar o seguinte starter no arquivo pom.xml:

1
2
3
4
<dependency>         
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-security</artifactId>
</dependency>

Configurando o Spring Security


Para configurar o Spring Security, necessitamos criar uma classe com a lógica de segurança, esta deve conter a annotation @EnableWebSecurity:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{

    private final String[] pagesAuthorizes  = {"/index.html"};
  
    @Override
    protected void configure(AuthenticationManagerBuilder auth) 
    throws Exception {
       auth.inMemoryAuthentication().withUser("usuario").password("123").roles("APP");
    }
 
    @Override
    public void configure(WebSecurity web) 
    throws Exception {
       web.ignoring().antMatchers("/resources/**");
    }
 
    @Override
    protected void configure(HttpSecurity http) 
    throws Exception {
       http.authorizeRequests()
           .antMatchers(this.pagesAuthorizes)
              .permitAll()
           .antMatchers("/**").hasRole("APP")
              .anyRequest()
              .authenticated()
           .and()
           .formLogin()
              .loginPage("/index.html")
              .loginProcessingUrl("/login")
              .failureHandler(this.customFailureHandler())
              .successHandler(this.customSuccessHandler())
              .permitAll()
           .and()
           .csrf()
              .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
           .and()
           .logout()
              .logoutSuccessHandler(this.customLogoutSuccessHandler())
              .invalidateHttpSession(true)
              .logoutRequestMatcher(new AntPathRequestMatcher("/logout"));
    }
   
    private AuthenticationFailureHandler customFailureHandler(){
       return (request, response, exception) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
    }

    private AuthenticationSuccessHandler customSuccessHandler(){
       return (request, response, authentication) -> response.sendError(HttpServletResponse.SC_OK);
    }
 
    private LogoutSuccessHandler customLogoutSuccessHandler(){
       return (request, response, authentication) -> response.sendError(HttpServletResponse.SC_OK);
    }
}

Vamos analisar cada item da classe acima:

1- Autenticação: Nessa parte, definimos o resource onde o Spring Security irá consulta os dados de autenticação, aqui podemos usar banco de dados, LDAP, etc, em nosso exemplo utilizamos uma persistência em memória:

1
2
3
4
5
 @Override
 protected void configure(AuthenticationManagerBuilder auth) 
 throws Exception {
    auth.inMemoryAuthentication().withUser("usuario").password("123").roles("APP");
 }

2- Exceção de Recursos: Nessa parte, marcamos para o Spring Security excluir da autenticação tudo que esteja dentro o path /resources/, este armazena os recursos estáticos como .js, .jpg, .css, etc:

1
2
3
4
5
 @Override
 public void configure(WebSecurity web) 
 throws Exception {
      web.ignoring().antMatchers("/resources/**");
 }


3- Lógicas de Acesso: Aqui é onde realizamos os mapeamentos de urls, lógicas de sucesso/falha e logout:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
 @Override
 protected void configure(HttpSecurity http) 
 throws Exception {
    http.authorizeRequests()
        .antMatchers(this.pagesAuthorizes)
           .permitAll()
        .antMatchers("/**").hasRole("APP")
           .anyRequest()
           .authenticated()
        .and()
        .formLogin()
           .loginPage("/index.html")
           .loginProcessingUrl("/login")
           .failureHandler(this.customFailureHandler())
           .successHandler(this.customSuccessHandler())
           .permitAll()
        .and()
        .csrf()
           .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
        .and()
        .logout()
           .logoutSuccessHandler(this.customLogoutSuccessHandler())
           .invalidateHttpSession(true)
           .logoutRequestMatcher(new AntPathRequestMatcher("/logout"));
 }

Vamos analisar as partes que envolvem as respostas em caso de falha, sucesso e logout.

A interface AuthenticationFailureHandler


Na linha 14 temos o método failureHandler(this.customFailureHandler()), que recebe como parâmetro uma AuthenticationFailureHandler, nossa implementação usando uma expressão lambda ficou da seguinte forma:

1
2
3
 private AuthenticationFailureHandler customFailureHandler(){
    return (request, response, exception) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
 }

Sem utilizar expressão lambda, basta criar um @Component como abaixo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@Component
public class AsyncAuthenticationFailureHandler implements AuthenticationFailureHandler {

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception)
    throws IOException, ServletException {
     
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
    }
}

Aqui falamos ao SpringSecurity, que em caso de falha no login, o mesmo deve apenas enviar como resposta um HttpStatus 401, que representa falha na autenticação.

A interface AuthenticationSuccessHandler


Na linha 15, temos o método successHandler(this.customSuccessHandler()), este manipula a resposta em caso de sucesso, nossa implementação usando uma expressão lambda ficou da seguinte forma:

1
2
3
 private AuthenticationSuccessHandler customSuccessHandler(){
    return (request, response, authentication) -> response.sendError(HttpServletResponse.SC_OK);
 }

Sem utilizar expressão lambda, basta criar um @Component como abaixo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@Component
public class AsyncAuthenticationSuccessHandler implements AuthenticationSuccessHandler{

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
    throws IOException, ServletException {
  
       response.sendError(HttpServletResponse.SC_OK);
    }
}

Este encaminha ao frontend um HttpStatus 200, que representa que a tentativa de autenticação foi realizada com sucesso.

A interface LogoutSuccessHandler


Na linha 22, temos o método logoutSuccessHandler(this.customLogoutSuccessHandler()), este manipula a resposta no processo de logout, veja a implementação:


1
2
3
private LogoutSuccessHandler customLogoutSuccessHandler(){
   return (request, response, authentication) -> response.sendError(HttpServletResponse.SC_OK);
}

Sem utilizar expressão lambda, basta criar um @Component como abaixo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@Component
public class AsyncLogoutSuccessHandler implements LogoutSuccessHandler{

  @Override
  public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
  throws IOException, ServletException {

     response.sendError(HttpServletResponse.SC_OK);
  }
}

Este encaminha ao frontend um HttpStatus 200, que representa  sucesso na operação de logout.

Realizado todo o procedimento acima, temos toda a camada do Spring Security pronta para processar as solicitações vindas do AngularJS.

Até a próxima.

Referências



Comentários

Postagens mais visitadas deste blog

Utilizando LocalDate, LocalDateTime e LocalTime na Prática

Tudo bem pessoal, hoje iremos avaliar e analisar operações de manipulação de Datas envolvendo a JavaTime API introduzida no Java 8.

Exemplos da java.time API
Já se passaram alguns anos desde o lançamento do Java 8 e mesmo após este período, há empresas que desconhecem o poder da java.time API, esta foi uma nova API de datas introduzida no Java 8 visando resolver problemas que tínhamos com as velhas classes Date e Calendar.

Vamos mostrar vários exemplos práticos do uso da API:
Criação de objetos No exemplo abaixo, estamos criando os objetos para manipulação de datas, perceba que temos 3 tipos, sendo LocalDate para manipular datas, LocalDateTime para manipular data/hora e LocalTime para horas:
LocalDate localDate = LocalDate.now(); LocalDateTime localDateTime = LocalDateTime.now(); LocalTime localTime = LocalTime.now();
Utilizando formatação Aqui realizamos a operação de formatação, perceba que não foi necessário utilizar a classe SimpleDateFormat, que usaríamos em conjunto com D…

Lendo e Manipulando arquivos CSV com Java

Olá hoje veremos como realizar a leitura e manipulação de arquivos CSV com Java, mostrando que as novas versões da plataforma Java deixaram algumas tarefas simples de serem realizadas.

Exemplo de Arquivo Hoje em dia é comum ao realizar integrações ou carga de dados o uso de arquivos CSV, isto porque, este arquivo possui uma estrutura de fácil entendimento e simples de manipular na maioria das plataformas de desenvolvimento.

Para realizar a leitura dos dados vamos manipular um arquivo com as seguintes colunas: namecpfagephone address:

name;cpf;age;phone;address caio;123456789;20;1145223643;AvenidaPaulista vinicius;147852369;18;1125253625;AvenidaManoel sandra;963258741;30;1174587858;RuaTeixeira regina;125478522;40;1145254536;RuaFernando fernando;785245563;42;1145253669;RuaPereira augusto;456123014;50;1125363633;AvenidaPaulinia maria;456123789;10;1125455525;AvenidaNossaSenhora
Para representar os dados em objetos Java iremos utilizar a seguinte classe:

importlombok.AllArgsConstruc…

Versionamento de Banco Dados com Flyway

Olá pessoal, hoje iremos analisar e aprender como realizar o versionamento e gerenciamento das bases de dados relacionais utilizando o framework Flyway.

Versionamento de Banco Dados Ao trabalhar com desenvolvimento de sistemas é comum realizarmos o versionamento, isso porque uma aplicação pode necessitar de correções, novas funcionalidades e evoluções, onde para garantir a compatibilidade e cuidar do seu ciclo de vida, geramos versões onde é especificado quais itens estão presentes em cada release.
Vamos imaginar seguinte cenário: Sistema ERP1.0.0: Entrega dos módulos Financeiro e RH;1.1.0: Entrega do módulo Contas a Receber. Perceba que a ideia é controlar o que cada versão possui de diferente, e para controlar essas mudanças no lado da aplicação existem várias técnicas e ferramentas, mas e para controlar as mudanças no lado do Banco de dados ? 
Para isso podemos utilizar ferramentas de versionamento que ajudam a realizar este controle, a ferramenta que iremos utilizar é o Flyway.
Con…