Lendo o manual do Spring Boot fiquei com um pouco de dúvida em relação ao funcionamento e ordem da autorização e autenticação com Spring Security. Estou estudando com o código abaixo. Classe SecurityConfiguration:
@EnableWebSecurity
@Configuration
public class SecurityConfigurations extends WebSecurityConfigurerAdapter {
...
//Configuracoes de autenticacao
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(autenticacaoService).passwordEncoder(new BCryptPasswordEncoder());
}
//Configuracoes de autorizacao
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(HttpMethod.GET, "/topicos").permitAll()
.antMatchers(HttpMethod.GET, "/topicos/*").permitAll()
.antMatchers(HttpMethod.POST, "/auth").permitAll()
.antMatchers(HttpMethod.GET, "/actuator/**").permitAll()
.anyRequest().authenticated()
.and().csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().addFilterBefore(new AutenticacaoViaTokenFilter(tokenService, usuarioRepository), UsernamePasswordAuthenticationFilter.class);
}
Filtro que criei para o processo de autenticação via token. O usuário envia usuário e senha, se estiver tudo certo o sistema retorna um token. Nas próximas requisições ele tem que enviar esse token para verificar se ele está ou não autenticado. Não coloquei todo o código, esse filtro somente valida o token, autentica e manda a requisição para frente:
public class AutenticacaoViaTokenFilter extends OncePerRequestFilter {
private TokenService tokenService;
private UsuarioRepository repository;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String token = recuperarToken(request);
boolean valido = tokenService.isTokenValido(token);
if (valido) {
autenticarCliente(token);
}
filterChain.doFilter(request, response);
}
...
Além dessa autenticação via Token com JWT eu posso usar a autenticação via sessão que o próprio Spring Boot oferece, onde ele já tem até um Controller próprio para isso e também já tem até uma página pronta de login. Com o JWT informei que meu filtro vem antes daquele que o Spring já tem internamente:
addFilterBefore(new AutenticacaoViaTokenFilter(tokenService, usuarioRepository), UsernamePasswordAuthenticationFilter.class);
Olhando no link abaixo, posso verificar que a autenticação do usuários é via filtro: https://docs.spring.io/spring-security/site/docs/4.2.1.RELEASE/reference/htmlsingle/#filter-ordering
Aqui na parte de autorização, pelo que entendi é feito com interceptadores: https://docs.spring.io/spring-security/site/docs/4.2.1.RELEASE/reference/htmlsingle/#tech-intro-access-control
No meu conhecimento, filtros vem antes dos controladores e os interceptores vem depois dos controladores e antes das chamadas das minhas ações. No manual tem esse trecho:
"É importante ressaltar que, no momento em que o AbstractSecurityInterceptorfor chamado, o SecurityContextHolder conterá um válido Authentication se o principal tiver sido autenticado."
Quando envio uma requisição POST para /auth ou um GET para /topicos que estão como permitALL() (código enviado acima), o que o Spring Security faz primeiro? Pois são ações que estão liberadas para qualquer um chamar, não precisa estar autenticado. De qualquer forma antes de analisar a autorização com os interceptadores, ou seja, antes de analisar se aquela URL está liberada ou não para um usuário autenticado, primeiro a requisição passa pelos filtros de autenticação (o que criei via JWT com Token ou o próprio do Spring) e só assim é verificado a autorização? O fluxo abaixo está correto?
Requisição GET para /topicos com um cliente que não está autenticado, não está enviando no cabeçalho da minha requisição um TOKEN para isso:
1 - Filtro de autenticação -> Não será autenticado pois não enviou um token válido, está vazio.
2 - doFilter() mando a requisição para frente sem fazer nada.
3 - Cai no Controller que responde o /topicos?
4 - Interceptador de autorização -> O /topicos está como permitAll(). Portando essa requisição mesmo que não possua um usuário autenticado será liberada para seguir o fluxo, chamarei a ação correspondente a ela. Se não tivesse liberado, se fosse uma requisição por exemplo para /alunos que não criei nenhuma regra retornaria sem autorização. E caso o usuário fornecesse um TOKEN válido lá no passo 1, o interceptador aqui saberia que tinha um Authentication válido e permitiria chamar a ação.
Obrigado.