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
Postar um comentário