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 doModelAndView.