En el desarrollo de aplicaciones web con Java EE, los filtros (Filter) son componentes fundamentales que nos permiten interceptar y procesar peticiones y respuestas HTTP. En este artículo, exploraremos cómo utilizar filtros para implementar un sistema de control de acceso en un proyecto de sistema de oficina (OA), específicamente para gestionar el estado de autenticación de los usuarios.
Concepto de Filtros en Java EE
Cada Servlet en una aplicación Java EE se encarga de resolver problemas de negocio específicos. Sin embargo, muchos de ellos requieren verificar si un usuario ha iniciado sesión antes de permitir el acceso a funcionalidades protegidas. En lugar de duplicar esta lógica de autenticación en cada Servlet, podemos utilizar un filtro para centralizar y reutilizar esta funcionalidad.
Los filtros actúan como una capa de procesamiento que se ejecuta antes o después de que un Servlet procese una petición. Se asocian a patrones de URL específicos y se ejecutan en un orden determinado. A diferencia de los Servlets, que no se instancian automáticamente al iniciar el servidor, los filtros se crean durante la inicialización de la aplicación.
El método chain.doFilter() es crucial en la implementación de filtros, ya que permite pasar el control al siguiente filtro en la cadena o, si no hay más filtros, al Servlet final. Es importante destacar que los filtros tienen mayor prioridad que los Servlets en el ciclo de vida de las peticiones.
Implementación de un Filtro de Autenticación
A continuación, presentamos un ejemplo de filtro que verifica si un usuario ha iniciado sesión antes de permitir el acceso a recursos protegidos:
package com.ejemplo.oa.filtros;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import java.io.IOException;
public class FiltroAutenticacion implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// Inicialización del filtro si es necesario
}
@Override
public void doFilter(Solicitud req, Respuesta res, CadenaFiltros chain)
throws IOException, ServletException {
SolicitudHTTP peticion = (SolicitudHTTP)req;
RespuestaHTTP respuesta = (RespuestaHTTP)res;
// Configurar codificación de caracteres
peticion.setCharacterEncoding("UTF-8");
respuesta.setContentType("text/html;charset=UTF-8");
// Obtener sesión sin crear una nueva si no existe
HttpSession sesion = peticion.getSession(false);
// Verificar si el usuario ha iniciado sesión
if(sesion != null && sesion.getAttribute("usuarioActual") != null) {
// Usuario autenticado, continuar con la cadena de filtros
chain.doFilter(peticion, respuesta);
} else {
// Usuario no autenticado, redirigir a página de inicio
respuesta.sendRedirect(peticion.getContextPath() + "/iniciar_sesion.jsp");
}
}
@Override
public void destroy() {
// Limpieza del filtro si es necesario
}
}
Configuración del Filtro en web.xml
Para registrar el filtro en la aplicación, podemos utilizar la configuración XML en el archivo web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<session-config>
<!-- Tiempo de vida de la sesión: 30 minutos -->
<session-timeout>30</session-timeout>
</session-config>
<!-- Página de bienvenida -->
<welcome-file-list>
<welcome-file>Bienvenido</welcome-file>
</welcome-file-list>
<!-- Configuración del filtro de autenticación -->
<filter>
<filter-name>filtroAutenticacion</filter-name>
<filter-class>com.ejemplo.oa.filtros.FiltroAutenticacion</filter-class>
</filter>
<filter-mapping>
<filter-name>filtroAutenticacion</filter-name>
<!-- Aplicar a todas las URLs bajo /departamento/* -->
<url-pattern>/departamento/*</url-pattern>
</filter-mapping>
</web-app>
Implementación de un Servlet de Departamento con Control de Acceso
A continuación, mostramos cómo implementar un Servlet que maneja operaciones relacionadas con departamentos, aprovechando el filtro de autenticación:
package com.ejemplo.oa.web.acciones;
import com.ejemplo.oa.utils.UtilidadesBD;
import com.ejemplo.oa.web.modelo.Departamento;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
@WebServlet(urlPatterns = {"/departamento/listar", "/departamento/detalle",
"/departamento/eliminar", "/departamento/guardar",
"/departamento/editar"})
public class ServletDepartamento extends HttpServlet {
@Override
protected void service(HttpServletRequest peticion, HttpServletResponse respuesta)
throws ServletException, IOException {
String rutaServlet = peticion.getServletPath();
if("/departamento/listar".equals(rutaServlet)) {
listarDepartamentos(peticion, respuesta);
} else if("/departamento/detalle".equals(rutaServlet)) {
mostrarDetalle(peticion, respuesta);
} else if("/departamento/eliminar".equals(rutaServlet)) {
eliminarDepartamento(peticion, respuesta);
} else if("/departamento/guardar".equals(rutaServlet)) {
guardarDepartamento(peticion, respuesta);
} else if("/departamento/editar".equals(rutaServlet)) {
editarDepartamento(peticion, respuesta);
}
}
private void editarDepartamento(HttpServletRequest peticion, HttpServletResponse respuesta)
throws ServletException, IOException {
Integer idDepto = Integer.parseInt(peticion.getParameter("idDepto"));
String nombreDepto = peticion.getParameter("nombreDepto");
String ubicacion = peticion.getParameter("ubicacion");
Connection conexion = null;
PreparedStatement sentencia = null;
ResultSet resultado = null;
int registrosAfectados = 0;
try {
conexion = UtilidadesBD.obtenerConexion();
conexion.setAutoCommit(false);
String sql = "update departamento set nombre = ?, ubicacion = ? where id = ?";
sentencia = conexion.prepareStatement(sql);
sentencia.setString(1, nombreDepto);
sentencia.setString(2, ubicacion);
sentencia.setInt(3, idDepto);
registrosAfectados = sentencia.executeUpdate();
conexion.commit();
} catch (SQLException e) {
if (conexion != null) {
try {
conexion.rollback();
} catch (SQLException ex) {
throw new RuntimeException(ex);
}
}
throw new RuntimeException(e);
} finally {
UtilidadesBD.cerrarConexion(conexion, sentencia, resultado);
}
if(registrosAfectados == 1) {
respuesta.sendRedirect(peticion.getContextPath() + "/departamento/listar");
} else {
respuesta.sendRedirect(peticion.getContextPath() + "/Error.jsp");
}
}
private void guardarDepartamento(HttpServletRequest peticion, HttpServletResponse respuesta)
throws ServletException, IOException {
Integer idDepto = Integer.parseInt(peticion.getParameter("idDepto"));
String nombreDepto = peticion.getParameter("nombreDepto");
String ubicacion = peticion.getParameter("ubicacion");
Connection conexion = null;
PreparedStatement sentencia = null;
ResultSet resultado = null;
int registrosAfectados = 0;
try {
conexion = UtilidadesBD.obtenerConexion();
conexion.setAutoCommit(false);
String sql = "insert into departamento(id, nombre, ubicacion) values(?,?,?)";
sentencia = conexion.prepareStatement(sql);
sentencia.setInt(1, idDepto);
sentencia.setString(2, nombreDepto);
sentencia.setString(3, ubicacion);
registrosAfectados = sentencia.executeUpdate();
conexion.commit();
} catch (SQLException e) {
if (conexion != null) {
try {
conexion.rollback();
} catch (SQLException ex) {
throw new RuntimeException(ex);
}
}
throw new RuntimeException(e);
} finally {
UtilidadesBD.cerrarConexion(conexion, sentencia, resultado);
}
if(registrosAfectados == 1) {
respuesta.sendRedirect(peticion.getContextPath() + "/departamento/listar");
} else {
respuesta.sendRedirect(peticion.getContextPath() + "/Error.jsp");
}
}
private void eliminarDepartamento(HttpServletRequest peticion, HttpServletResponse respuesta)
throws ServletException, IOException {
Connection conexion = null;
PreparedStatement sentencia = null;
ResultSet resultado = null;
String idDepto = peticion.getParameter("idDepto");
int registrosAfectados = 0;
try {
conexion = UtilidadesBD.obtenerConexion();
conexion.setAutoCommit(false);
String sql = "delete from departamento where id = ?";
sentencia = conexion.prepareStatement(sql);
sentencia.setString(1, idDepto);
registrosAfectados = sentencia.executeUpdate();
conexion.commit();
} catch (SQLException e) {
if (conexion != null) {
try {
conexion.rollback();
} catch (SQLException ex) {
throw new RuntimeException(ex);
}
}
throw new RuntimeException(e);
} finally {
UtilidadesBD.cerrarConexion(conexion, sentencia, resultado);
}
if(registrosAfectados == 1) {
respuesta.sendRedirect(peticion.getContextPath() + "/departamento/listar");
} else {
respuesta.sendRedirect(peticion.getContextPath() + "/Error.jsp");
}
}
private void mostrarDetalle(HttpServletRequest peticion, HttpServletResponse respuesta)
throws ServletException, IOException {
Connection conexion = null;
PreparedStatement sentencia = null;
ResultSet resultado = null;
String idDepto = peticion.getParameter("idDepto");
try {
conexion = UtilidadesBD.obtenerConexion();
String sql = "select * from departamento where id = ?";
sentencia = conexion.prepareStatement(sql);
sentencia.setString(1, idDepto);
resultado = sentencia.executeQuery();
if(resultado.next()) {
String nombreDepto = resultado.getString("nombre");
String ubicacion = resultado.getString("ubicacion");
Departamento depto = new Departamento(idDepto, nombreDepto, ubicacion);
peticion.setAttribute("departamento", depto);
}
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
UtilidadesBD.cerrarConexion(conexion, sentencia, resultado);
}
String tipoOperacion = peticion.getParameter("t");
if("e".equals(tipoOperacion)) {
peticion.getRequestDispatcher("/detalle.jsp").forward(peticion, respuesta);
}
if("m".equals(tipoOperacion)) {
peticion.getRequestDispatcher("/editar.jsp").forward(peticion, respuesta);
}
}
private void listarDepartamentos(HttpServletRequest peticion, HttpServletResponse respuesta)
throws ServletException, IOException {
Connection conexion = null;
PreparedStatement sentencia = null;
ResultSet resultado = null;
List<departamento> listaDepartamentos = new ArrayList<>();
try {
conexion = UtilidadesBD.obtenerConexion();
String sql = "select * from departamento";
sentencia = conexion.prepareStatement(sql);
resultado = sentencia.executeQuery();
while(resultado.next()) {
String idDepto = resultado.getString("id");
String nombreDepto = resultado.getString("nombre");
String ubicacion = resultado.getString("ubicacion");
Departamento depto = new Departamento(idDepto, nombreDepto, ubicacion);
listaDepartamentos.add(depto);
}
peticion.setAttribute("departamentos", listaDepartamentos);
peticion.getRequestDispatcher("/listado.jsp").forward(peticion, respuesta);
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
UtilidadesBD.cerrarConexion(conexion, sentencia, resultado);
}
}
}
</departamento>
La implementación de filtros en Java EE proporciona una manera eficiente y reutilizable de gestionar aspectos transversales como la autanticación y autorización. Al centralizar esta lógica en filtros, podemos mantener nuestros Servlets enfocados en la lógica de negocio específica, mejorando así la mantenibilidad y extensibilidad de la aplicación.