Interceptores e Filtros no Spring MVC

Spring Interceptores

Visão Geral dos Interceptores

Os interceptores no Spring são baseados na técnica de Programação Orientada a Aspectos (AOP), funcionando sobretudo através de proxies. Eles atuam nas requisições da camada de controladores (Controller), permitindo, por exemplo, a verificação de permissões de acesso a endpoints.

Implementação de um Interceptor

Para criar um interceptor, defina uma classe que implemente a interface HandlerInterceptor e sobrescreva seus métodos principais:

@Component
public class InterceptadorDeExemplo implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object manipulador) throws Exception {
        System.out.println("Execução antes do processamento do controlador...");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object manipulador, ModelAndView modelAndView) throws Exception {
        // Não é executado se o controlador lançar uma exceção.
        System.out.println("Execução após o processamento do controlador...");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object manipulador, Exception excecao) throws Exception {
        // Executado após a conclusão da requisição, similar ao bloco 'finally'.
        System.out.println("Execução após a conclusão da requisição...");
    }
}

Registro do Interceptor

O interceptor precisa ser registrado no contexto Spring. Crie uma classe de configuração implementando WebMvcConfigurer:

@Configuration
public class ConfiguracaoMvc implements WebMvcConfigurer {

    @Autowired
    private InterceptadorDeExemplo interceptadorExemplo;

    @Override
    public void addInterceptors(RegistryInterceptadores registro) {
        registro.addInterceptor(interceptadorExemplo).addPathPatterns("/**");
    }
    // ... outros métodos de configuração, como para recursos estáticos, se necessário.
}

Caso de Uso: Interceptor contra Envio Repetido

Um interceptor abstrato pode ser criado para verificar anotações customizadas:

@Component
public abstract class InterceptadorDeEnvioRepetido implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object manipulador) throws Exception {
        if (manipulador instanceof HandlerMethod) {
            HandlerMethod metodoManipulador = (HandlerMethod) manipulador;
            Metodo metodo = metodoManipulador.getMethod();
            EnvioRepetido anotacao = metodo.getAnnotation(EnvioRepetido.class);
            if (anotacao != null && this.ehEnvioRepetido(request, anotacao)) {
                RespostaAjax resultado = RespostaAjax.error(anotacao.mensagem());
                UtilitarioServlet.renderizarString(response, JSONObject.toJSONString(resultado));
                return false;
            }
        }
        return true;
    }

    // Método abstrato para implementação da lógica específica de verificação.
    public abstract boolean ehEnvioRepetido(HttpServletRequest request, EnvioRepetido anotacao);
}

A anotação customizada correspondente:

@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EnvioRepetido {
    int intervalo() default 5000;
    String mensagem() default "Operação já em andamento. Aguarde.";
}

Spring Filtros (Filters)

Visão Geral dos Filtros

Filtros são um componente da especificação Java Servlet, baseados em callbacks de função. Eles interceptam e modificam requisições e respostas antes de atingirem o DispatcherServlet do Spring. Podem atuar sobre qualquer recurso gerenciado pelo servidor web (JSP, HTML, imagens, etc.).

Ciclo de Vida

  • init(): Inicializa a instância do filtro, chamado pelo contêiner (ex: Tomcat) na inicialização.
  • doFilter(): Lógica principal de interceptação e processamento.
  • destroy(): Chamado quando o contêiner é desligado.

Formas de Registro de um Filtro

1. Usando a anotação @WebFilter

Declare a classe com @WebFilter e habilite o scan com @ServletComponentScan na classe principal:

@WebFilter(urlPatterns = "/api/*")
public class FiltroDeVisitante implements Filter {

    @Override
    public void init(FilterConfig configuracao) throws ServletException {}

    @Override
    public void doFilter(ServletRequest requisicao, ServletResponse resposta, FilterChain cadeia) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) requisicao;
        String ipVisitante = req.getRemoteAddr();
        System.out.println("Requisição do IP: " + ipVisitante);
        cadeia.doFilter(requisicao, resposta); // Passa para o próximo filtro ou recurso.
    }

    @Override
    public void destroy() {}
}

@SpringBootApplication
@ServletComponentScan
public class Aplicacao {
    public static void main(String[] args) {
        SpringApplication.run(Aplicacao.class, args);
    }
}

2. Usando @Component

Anote a classe do filtro com @Component e use @Order para controlar a ordem de execução:

@Order(1)
@Component
public class FiltroDeAutorizacao implements Filter {
    // ... implementação similar à anterior.
}

3. Usando Classe de Configuração Java (@Configuration + @Bean)

Oferece maior controle, como definir parâmetros de inicialização e ordem via FilterRegistrationBean:

public class FiltroDeLogging implements Filter {
    @Override
    public void init(FilterConfig configuracao) throws ServletException {}

    @Override
    public void doFilter(ServletRequest requisicao, ServletResponse resposta, FilterChain cadeia) throws IOException, ServletException {
        // Lógica de logging.
        cadeia.doFilter(requisicao, resposta);
    }

    @Override
    public void destroy() {}
}

@Configuration
public class ConfiguracaoDeFiltros {
    @Bean
    public FilterRegistrationBean<FiltroDeLogging> registroFiltroLogging() {
        FilterRegistrationBean<FiltroDeLogging> registro = new FilterRegistrationBean<>();
        registro.setFilter(new FiltroDeLogging());
        registro.addUrlPatterns("/sistema/*", "/admin/*");
        registro.setOrder(2);
        registro.addInitParameter("param", "valor");
        return registro;
    }
}

Sobre OncePerRequestFilter

Para garantir que um filtro seja executado apenas uma vez por requisição, especialmente em cenários com forwards e includes, herde de OncePerRequestFilter. É a abordagem recomendada no ecossistema Spring:

@Component
public class FiltroDeTokenJwt extends OncePerRequestFilter {

    @Autowired
    private ServicoDeToken servicoDeToken;

    @Override
    protected void doFilterInternal(HttpServletRequest requisicao, HttpServletResponse resposta, FilterChain cadeia)
            throws ServletException, IOException {
        UsuarioLogado usuario = servicoDeToken.obterUsuarioLogado(requisicao);
        if (usuario != null && SecurityUtils.getAuthentication() == null) {
            servicoDeToken.verificarToken(usuario);
            UsernamePasswordAuthenticationToken autenticacao = new UsernamePasswordAuthenticationToken(
                    usuario, null, usuario.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(autenticacao);
        }
        cadeia.doFilter(requisicao, resposta);
    }
}

Comparação Fundamental: Interceptores vs. Filtros

Característica Filtro (Filter) Interceptador (Interceptor)
Princípio de Implementação Baseado em callbacks (interface Servlet) Baseado em proxies/reflexão (Spring AOP)
Escopo de Uso Depende do servidor web (Servlet Container) Gerenciado pelo Spring Container
Momento da Execução Antes da requisição chegar ao DispatcherServlet Após o DispatcherServlet, antes do Controller
Recursos Interceptados Quase todos os recursos (JSP, estáticos, etc.) Apenas requisições que passam pelo DispatcherServlet (geralmente Controllers)
Injeção de Beans Spring Funciona corretamente via @Autowired Pode causar NullPointerException se não for gerenciado como Bean pelo Spring
Controle de Ordem Anotação @Order ou FilterRegistrationBean.setOrder() Método .order() no InterceptorRegistry ou implementação de Ordered

Observação importante: Ao usar múltiplos interceptadores, a ordem de execução do método postHandle é inversa à do preHandle.

Quando Usar Cada Um?

  • Use Filtros para tarefas de baixo nível, não relacionadas à lógica de negócio Spring, que precisam ocorrer antes mesmo da requisição ser roteada pelo Spring MVC. Exemplos: autenticação baseada em token JWT em endpoints não-Spring, logging bruto de requisição, compressão de resposta, controle de acesso a recursos estáticos.
  • Use Interceptores para lógica que interage com o contexto de execução do Spring MVC e dos Controllers. Exemplos: logging detalhado de parâmetros e respostas dos Controllers, verificação de permissões baseada em anotações (como @PreAuthorize), controle de ambinete, e manipulação do ModelAndView.

Tags: Spring MVC Java Servlet Interceptadores Filtros Annotation

Publicado em 7-3 23:59