Excluir URLs con filtro en Spring
En esta entrada vamos a ver como podemos excluir urls con filtro en Spring. Cuando estamos creando una aplicación en donde tenemos autenticación/autorización, monitorización o validaciones, necesitamos excluir algunas urls que no queremos que formen parte de esa validación, o que no tenga que se autenticada. Para estos casos Spring nos proporciona una manera sencilla para poder excluir o incluir un conjunto de urls en la ejecución de nuestra aplicación.
Vamos a ver diferentes ejemplos en los que en función de si queremos excluir una url o no, añadimos un logging del traceId en nuestra aplicación.
Creación de filtro en Spring
Tanto para excluir como para realizar inclusiones de URL’s vamos a hacer uso de la clase OncePerRequestFilter. Esta clase nos proporciona una serie de métodos que serán los que vamos a sobreescribir para nuestros filtros.
public class LogTraceIdFilter extends OncePerRequestFilter { //add logic }
Creación de Filtro en Spring excluyendo URL’s
Partiendo de la clase anterior (CustomFilterExclusionInclusion) vamos a crear un filtro que excluya las peticiones del endpoint actuator de beans de Spring, es decir, actuator/beans.
@Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String path = request.getRequestURI(); if ("/actuator/beans".equals(path)) { filterChain.doFilter(request, response); return; } String traceId = request.getHeader("x-trace-id"); log.debug("TraceId {}", traceId); filterChain.doFilter(request, response); }
Una vez hemos creado el filtro es necesario registrarlo para eso creamos un bean del tipo FilterRegistrationBean, en donde lo activamos para todas las peticiones haciendo uso de *:
@Bean public FilterRegistrationBean<LogTraceIdFilter> logFilter() { FilterRegistrationBean<LogTraceIdFilter> registrationBean = new FilterRegistrationBean<>(); registrationBean.setFilter(new LogTraceIdFilter()); registrationBean.addUrlPatterns("*"); return registrationBean; }
En esta aproximación hemos dicho que si el endpoint invocado es /actuator/beans, no haga nada. Sin duda, esta aproximación funciona sin ningún problema pero no es la más elegante ya que mezclamos lógica y funcionalidad en el mismo método. Para solventar esta casuística cuando únicamente queremos excluir una URL podemos hacer uso de shouldNotFilter.
Excluir URL con shouldNotFilter
Existe un método dentro de la clase OncePerRequestFilter que nos permite definir que urls podemos excluir de la ejecución. Este método es shouldNotFilter.
Si hacemos uso de doFilterInternal estamos obligados a meter condiciones, pero con el uso de shouldNotFilter podemos aislar la lógica de doFilterInternal y añadir en otro método las urls a excluir.
Por ejemplo:
@Override protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { String path = request.getRequestURI(); return "/actuator/beans".equals(path); }
Haciendo uso de shouldNotFilter, dividimos las responsabilidades delegando en este método aquellas urls que vamos a excluir.
El código anterior también podría haber sido realizado con AntPathMatcher. AntPathMatcher
en Spring proporciona una forma eficiente y versátil de manejar la lógica de coincidencia de rutas y URLs utilizando la sintaxis de coincidencia de ant:
List<String> excludeUris = List.of("actuator/beans"); if (!CollectionUtils.isEmpty(this.excludeUris)) { AntPathMatcher antPathMatcher = new AntPathMatcher(); String url = srequest.getURI().getPath(); log.debug("The url to analyze is {} ", url); Predicate<String> patternMatchUrl = e -> antPathMatcher.match(e, url); return this.excludeUris.stream().anyMatch(patternMatchUrl); } return false;
Al igual que en el caso anterior es necesario registrar el bean:
@Bean public FilterRegistrationBean<LogTraceIdFilter> logFilter() { FilterRegistrationBean<LogTraceIdFilter> registrationBean = new FilterRegistrationBean<>(); registrationBean.setFilter(new LogTraceIdFilter()); registrationBean.addUrlPatterns("*"); return registrationBean; }
Creación de filtro en Spring para URL’s específicas
A continuación vamos a crear crear el código anterior para que únicamente se ejecute en determinadas urls.
Sobreescribimos el método doFilterInternal para hacer log del traceId de la petición:
@Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String path = request.getRequestURI(); String traceId = request.getHeader("x-trace-id"); log.debug("TraceId {}", traceId); filterChain.doFilter(request, response); }
Y a continuación creamos un bean de tipo FilterRegistrationBean en el que añadimos las urls para las que queremos que se ejecute nuestro filtro. Es decir, para aquellas urls que hace match. En este caso únicamente queremos que se haga log del traceId para todos los endpoints de actuator.
@Bean public FilterRegistrationBean<LogTraceIdFilter> logFilter() { FilterRegistrationBean<LogTraceIdFilter> registrationBean = new FilterRegistrationBean<>(); registrationBean.setFilter(new LogTraceIdFilter()); registrationBean.addUrlPatterns("/actuator/*"); return registrationBean; }
Creación de filtro en Spring para todas las URLs
A continuación vamos a crear un filtro que se ejecutará para todas las urls. El filtro a crear analizará la cabecera X-Code, si la cabecera tiene una longitud menor de 5 lanzaremos una excepción.
@Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String code = request.getHeader("X-Code"); if (code.length() < 5 ) { response.sendError(HttpStatus.BAD_REQUEST.value(), "Invalid x-code"); return; } filterChain.doFilter(request, response); } }
Una vez hemos creado nuestro filtro, vamos a registrarlo para todas las peticiones:
@Bean public FilterRegistrationBean<LogTraceIdFilter> logFilter() { FilterRegistrationBean<LogTraceIdFilter> registrationBean = new FilterRegistrationBean<>(); registrationBean.setFilter(new LogTraceIdFilter()); registrationBean.addUrlPatterns("*"); return registrationBean; }
El * nos permite que nuestro filtro se ejecute para todas las peticiones.
Conclusión
En esta entrada sobre Excluir URLs con filtro en Spring hemos visto como podemos crear un filtro en función de nuestras necesidades y que se ejecute para todas las peticiones o para algunas en concreto.
Si necesitas más información puedes escribirnos un comentario o un correo electrónico a refactorizando.web@gmail.com o también nos puedes contactar por nuestras redes sociales Facebook o twitter y te ayudaremos encantados!