Los filtros interceptan peticiones antes y después de que lleguen al Servlet o a la JSP. Los listeners reaccionan a eventos del ciclo de vida de la aplicación, la sesión y la petición.
Filtros: casos de uso comunes
- Autenticación y autorización (como vimos antes).
- Logging de peticiones.
- Compresión GZIP de la respuesta.
- Establecer codificación de caracteres.
- Cabeceras de seguridad (CORS, CSP, X-Frame-Options).
@WebFilter("/*")
public class LoggingFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
long inicio = System.currentTimeMillis();
chain.doFilter(req, resp); // ejecutar el Servlet/JSP
long duracion = System.currentTimeMillis() - inicio;
System.out.printf("[%s] %s %s → %dms%n",
LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME),
request.getMethod(),
request.getRequestURI(),
duracion
);
}
}
Filtro de cabeceras de seguridad
@WebFilter("/*")
public class SecurityHeadersFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) resp;
response.setHeader("X-Content-Type-Options", "nosniff");
response.setHeader("X-Frame-Options", "SAMEORIGIN");
response.setHeader("X-XSS-Protection", "1; mode=block");
response.setHeader("Referrer-Policy", "strict-origin-when-cross-origin");
// HSTS solo si usas HTTPS
// response.setHeader("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
chain.doFilter(req, resp);
}
}
Listeners
// Escuchar eventos del contexto (aplicación)
@WebListener
public class AppContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
// Se ejecuta al arrancar la aplicación
ServletContext ctx = sce.getServletContext();
ctx.setAttribute("version", "1.0.0");
ctx.setAttribute("arranque", LocalDateTime.now());
System.out.println("Aplicación iniciada");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("Aplicación detenida");
// Cerrar conexiones a BD, apagar hilos, etc.
}
}
// Escuchar eventos de sesión
@WebListener
public class SessionListener implements HttpSessionListener {
private final AtomicInteger activas = new AtomicInteger();
@Override
public void sessionCreated(HttpSessionEvent se) {
int n = activas.incrementAndGet();
System.out.println("Nueva sesión. Activas: " + n);
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
activas.decrementAndGet();
}
}
