sábado, 25 de mayo de 2013

Filtros de Tomcat - Parte I

Supongamos por un momento que es necesario cortar un árbol y se poseen las siguientes herramientas: un Hacha y un Serrucho.

¿Cuál herramienta es la más eficiente?

La mayoría de las personas considerarán el serrucho como la más eficiente. ¿Por qué? Básicamente porque todos conocemos las capacidades de un serrucho y podemos compararla con el hacha. Supongamos ahora que no conocemos ninguna de las herramientas y debemos elegir. La elección no es tan simple porque no conocemos las capacidades de cada una. El sentido común no nos permitiría hacer una elección correcta tampoco.

Lo anterior es un ejemplo muy simple de lo importante que es conocer (y tener) las herramientas adecuadas para realizar una tarea.

Hace algunas semanas atrás, un cliente solicitó una modificación a uno de los productos de la empresa en la que trabajo. La modificación implicaba modificar todas las páginas .jsp de la aplicación y, obviamente, había un plazo que cumplir, por lo que, la intervención manual de todas las páginas no era una opción. Había que buscar otra alternativa. ¿La solución? Filtros de Tomcat.  

1. Filtros de Tomcat
El servidor web Apache Tomcat provee una infraestructura denominada Filtros que permite interceptar el flujo de entrada y salida desde y hacia una página (.jsp) e intervenirlo. La gracia es que el filtro se programa una vez y permite realizar la tarea en todas las páginas existentes en el contexto.

La infraestructura de filtros está disponible desde la Servlet API 2.3 y fue mejorada en la versión 2.4, permitiendo que los filtros sean aplicados incluso en el traspaso de las solicitudes entre las páginas (por medio de las directivas forward o include). Los diagramas siguientes muestran cómo operan los filtros en el flujo de procesamiento de una página en las dos versiones.



2. Ejemplos de Filtros
Considerando el flujo anterior, un Filtro se puede construir para realizar diversas tareas. Algunos ejemplos simples son los siguientes:
  • Validaciones de Seguridad. Validar que cada página cumpla una condición determinada, por ejemplo, que el usuario esté autenticado.
  • Compresión de Salidas/Entradas. Comprimir un archivo automáticamente para minimizar el tiempo de descarga.
  • Registro de Eventos. Generar un log de auditoría respecto con la información de los accesos a las páginas de la aplicación.
  • Modificación/Intervención de Salidas/Entradas. Eliminación de caracteres especiales, sobrantes, limpieza de HTML, etc.
La implementación de un filtro implica las siguientes actividades:
  • Construcción de la clase que implementará el filtro con la funcionalidad deseada.
  • Configuración del filtro en Tomcat para que realice su tarea
Obviamente, la construcción del filtro debe realizarse primero que la configuración, sin embargo, voy a describir la configuración primero porque es relevante para entender el "encadenamiento" de los filtros en el procesamiento y la construcción correspondiente.

3. Configuración del Filtro
La configuración del filtro se realiza en el archivo web.xml del contexto (dentro de la carpeta WEB-INF/). Se debe agregar una configuración como la siguiente para cada filtro que se quiera habilitar:

<filter>
  <filter-name>NOMBRE</filter-name>
  <filter-class>CLASE</filter-class>
  <init-param>
    <param-name>PARAM_1</param-name>
    <param-value>VALOR_1</param-value>
  </init-param>

  :
  <init-param>
    <param-name>PARAM_n</param-name>
    <param-value>VALOR_n</param-value>
  </init-param>
</filter>


<filter-mapping>
  <filter-name>NOMBRE</filter-name>
  <url-pattern>PATRON</url-pattern>
</filter-mapping>


3.1 Sección <filter>.
Permite configurar un filtro en el contexto.
  • NOMBRE. Corresponde a un nombre lógico que identifica la acción/objetivo que realiza el filtro. Por ejemplo: LoginFilter, RegExFilter, HitCountFilter, etc.
  • CLASE. Corresponde al nombre de la clase que implementa el filtro. Obviamente, debe ser una clase que esté disponible para el contexto de la aplicación.
  • PARAM_i y VALOR_i. Se pueden agregar parámetros de configuración relevantes para el filtro agregando nodos del tipo <init-param>. Cada nodo contiene un nombre (PARAM_i) y un valor (VALOR_i).
3.2 Sección <filter-mapping>.
Permite configurar a qué páginas se aplicará el filtro.
  • NOMBRE. Corresponde al nombre lógico del filtro que se indicó en la sección anterior. Debe ser el mismo para que aplique correctamente.
  • PATRON. Corresponde al patrón de páginas para las cuales se desea que se aplique el filtro. Un ejemplo es /* que indica que aplica a todas las páginas del contexto.
Un aspecto importante de la configuración en el archivo web.xml es que el orden en el que se declaran los filtros. Por ejemplo, supongamos que tenemos dos filtros: HitCountFilter y LoginFilter. El primero registra el número de accesos a las páginas del sitio y el segundo valida que un usuario esté autenticado. Veamos dos configuraciones posibles en el web.xml:


La configuración a) implica que primero se registrarán los accesos y luego se validará al usuario. La configuración b) implica que primero se validará al usuario y luego se registrarán los accesos.

Lo anterior se denomina encadenamiento de filtros (Filter Chain) y establece el orden en el que se deben aplicar los filtros. Lo importante de esto es que los filtros se aplican en cadena sobre la entrada y, luego, de manera inversa sobre la salida. Considerando el ejemplo a) anterior, se produce la siguiente secuencia:


La entrada (E1) es recibida y procesada por HitCounter. El resultado (E2) es entregado a LoginFilter quien la procesa. El resultado (E3) es entregado a la página.jsp para su procesamiento. A esta altura, la ejecución de la página.jsp es similar a la ejecución que se realizaría si no hubiera filtros configurados, es decir, la página.jsp se programa como una página normal, independiente de los filtros que podrían haber sido configurados previamente. El flujo de salida opera de la misma manera permitiendo realizar acciones sobre el resultado (output) de la página.jsp. Ejemplos de acciones posibles a este nivel son la compresión del HTML (eliminando espacios en blanco) o el reemplazo de contenido por otro utilizando expresiones regulares.

4. Construcción del Filtro 
Un filtro se construye heredando de la clase javax.servlet.Filter y, en él, se deben implementar tres métodos:
  • init(FilterConfig filterConfig).
    Este método se ejecuta cuando el contexto es cargado por el servidor y sólo se ejecuta una vez. El parámetro filterConfig permite acceder a las configuraciones que se hayan realizado para el filtro en el archivo web.xml por medio de la secciones <init-param/>. 
  • doFilter(ServletRequest request, ServletResponse response, FilterChain chain).
    Este método se ejecuta cuando se requiere aplicar el filtro a la entrada (request) y a la salida (response). El parámetro chain es el que permite traspasar el control al siguiente filtro en la cadena. En casos específicos, se puede interrumpir el flujo programáticamente. Por ejemplo, si el filtro LoginFilter identifica que el usuario no está autenticado, no es necesario ejecutar la página .jsp correspondiente.
  • destroy(). Este método se ejecuta cuando se baja el contexto y puede/debe ser utilizado para liberar recursos que se hayan utilizado para la operación del filtro. Por ejemplo, cachés, contadores, etc.
En la próxima parte describiré un ejemplo práctico de cómo hacer un filtro de autenticación de usuarios que permita validar, de una manera simple y centralizada, que el acceso a las páginas lo realicen únicamente usuarios que se hayan autenticado correctamente.

No hay comentarios.: