Solucionado (ver solução)
Solucionado
(ver solução)
3
respostas

[Dúvida] UsernamePasswordAuthenticationFilter gera erro 403 antes do Tratador de Erros

Olá, ao implementar os outros métodos do TratadorDeErros percebi que os métodos implementados para retornar erros como 403 e 401 para o usuário não são chamados pois o UsernamePasswordAuthenticationFilter está retornando 403 para o usuário antes de passar pelo TratadorDeErros.

Poderiam me ajudar?

3 respostas

Olá, Leonardo!

Entendo a sua frustração com o UsernamePasswordAuthenticationFilter retornando um erro 403 antes de passar pelo seu TratadorDeErros. Isso acontece porque o filtro de autenticação é executado antes do controlador, e, portanto, antes do seu tratador de erros personalizado.

Para contornar esse problema, você pode implementar um AuthenticationEntryPoint personalizado. O AuthenticationEntryPoint é responsável por iniciar o processo de autenticação quando uma solicitação não autenticada tenta acessar um recurso protegido. Ao criar um EntryPoint personalizado, você pode manipular a resposta de erro antes de ser enviada ao cliente.

Aqui está um exemplo de como você pode fazer isso:

import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response,
                         AuthenticationException authException) throws IOException {
        response.sendError(HttpServletResponse.SC_FORBIDDEN, "Acesso negado! Você não tem permissão para acessar este recurso.");
    }
}

Depois de criar o EntryPoint personalizado, você precisa configurá-lo na sua classe de configuração de segurança:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@EnableWebSecurity
@Configuration
public class SecurityConfigurations {

    @Autowired
    private CustomAuthenticationEntryPoint customAuthenticationEntryPoint;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            // configs atuais...
            .and()
            .exceptionHandling().authenticationEntryPoint(customAuthenticationEntryPoint);
    }
}

Com isso, o seu CustomAuthenticationEntryPoint será chamado quando uma tentativa de acesso não autorizada ocorrer, permitindo que você controle a resposta de erro.

Espero ter ajudado e bons estudos!

Legal, nesse meio tempo encontrei outra solução, poderia me ajudar a identificar qual das duas é melhor?

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    @Autowired
    private FilterSecurity filterSecurity;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http.csrf(csrf -> csrf.disable())
                .sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .authorizeHttpRequests(req -> {
                    req.requestMatchers("/login").permitAll();
                    req.anyRequest().authenticated();
                })
                .exceptionHandling(eh -> eh
                        .authenticationEntryPoint((request, response, authException) -> {
                            response.setStatus(HttpStatus.UNAUTHORIZED.value());
                            response.getWriter().write("Erro na validação do Token: " + authException.getMessage());
                        })
                        .accessDeniedHandler((request, response, accessDeniedException) -> {
                            response.setStatus(HttpStatus.FORBIDDEN.value());
                            response.getWriter()
                                    .write("Acesso negado: " + accessDeniedException.getMessage());
                        })
                        )
                        
                .addFilterBefore(filterSecurity, UsernamePasswordAuthenticationFilter.class)
                .build();
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
        return configuration.getAuthenticationManager();
    };

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

} 
solução!

As duas são similares, pois configuram o tratamento para erros relacionados com autenticação. Eu particulamente gosto de deixar em classes separadas, para não deixar o código das configurações de segurança muito extenso. Mas é mais uma questão de preferência pessoal mesmo.

Bons estudos!