Solucionado (ver solução)
Solucionado
(ver solução)
1
resposta

Ordem do fluxo de autenticação e autorização no Spring Security

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.

1 resposta
solução!

Oi Eduardo,

Meio complicada a pergunta, pois precisaria analisar o código fonte do Spring Security, talvez até debugando, para saber exatamente o passo a passo em cada cenário.

Vou verificar se encontro algum fluxograma mostrando as chamadas de classes no processo de autenticação/autorização do Spring.

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