Implementación de Control de Acceso Mediante Interceptors en Spring MVC

En aplicaciones web desarrolladas con Spring MVC, es común verificar el estado de inicio de sesión de un usuario mediante cookies o sesiones. Un enfoque anterior podría ser la validación directa en la vista, utilizando etiquetas JSTL para redirigir a la página de inicio de sesión si el usuario no está autenticado. Por ejemplo:


<c:if test="${empty sessionScope.user || !sessionScope.user.loggedIn}">
   <c:redirect url="/auth/login"/>
</c:if>
<c:if test="${not empty sessionScope.user && sessionScope.user.loggedIn}">
   <!-- Contenido para usuarios autenticados -->
</c:if>
	

Sin embargo, este método presenta una limitación: si la sesión expira, la renderización de la página puede interrumpirse abruptamente sin una redirección adecuada a la pantalla de inicio de sesión. Para abordar esta deficiencia, se puede implemantar un mecanismo de control de acceso centralizado utilizando interceptors de Spring MVC.

Configuración del Interceptor

La configuración de los interceptors se realiza típicamente en un archivo de configuración de Spring. A continuación, se muestra un ejemplo:


<!-- Se definen los interceptors dentro del namespace mvc -->
<mvc:interceptors>
   <!-- Un interceptor específico para el control de autenticación -->
   <mvc:interceptor>
       <!-- Mapeo de URLs: '/**' intercepta todas las rutas.
            Es crucial excluir recursos estáticos y la ruta de inicio de sesión. -->
       <mvc:mapping path="/**"/>
       <!-- Bean que representa la implementación del interceptor -->
       <bean class="com.example.app.interceptor.AuthInterceptor">
           <!-- Propiedad para especificar las URLs que no deben ser interceptadas -->
           <property name="excludedPaths">
               <list>
                   <value>/auth/login</value>
                   <value>/resources/**</value> <!-- Para archivos estáticos como CSS, JS, imágenes -->
               </list>
           </property>
       </bean>
   </mvc:interceptor>
</mvc:interceptors>
	

Implementación del Interceptor

La clase del interceptor debe implementar la interfaz HandlerInterceptor de Spring MVC. Los métodos clave son:

  • preHandle(): Se ejecuta antes de que el controlader procese la solicitud. Si devuelve false, la cadena de interceptors y el controlador no se ejecutarán.
  • postHandle(): Se ejecuta después de que el controlador ha procesado la solicitud, pero entes de que la vista se renderice.
  • afterCompletion(): Se ejecuta después de que la vista se ha renderizado completamente y la respuesta se ha enviado al cliente.

A continuación, se presenta una implementación de ejemplo para el control de autenticación:


package com.example.app.interceptor;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.List;

public class AuthInterceptor implements HandlerInterceptor {

   private static final Logger log = LoggerFactory.getLogger(AuthInterceptor.class);

   private List<String> excludedPaths;

   /**
    * Se ejecuta después de completar el procesamiento de la solicitud y la renderización de la vista.
    * Se llama incluso si ocurre una excepción en un interceptor anterior.
    */
   @Override
   public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
                               Object handler, Exception ex) throws Exception {
       // Lógica de limpieza si es necesaria
   }

   /**
    * Se ejecuta después de que el controlador ha manejado la solicitud, pero antes de renderizar la vista.
    * Útil para modificar el modelo o la vista.
    */
   @Override
   public void postHandle(HttpServletRequest request, HttpServletResponse response,
                          Object handler, ModelAndView modelAndView) throws Exception {
       // No se requiere lógica específica para el control de acceso en este método.
   }

   /**
    * Punto de entrada principal para la lógica de control de acceso.
    * Verifica si la solicitud debe ser procesada o redirigida.
    * @return true si la solicitud debe continuar, false si debe detenerse.
    */
   @Override
   public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                            Object handler) throws Exception {

       String requestPath = request.getRequestURI();

       // Verificar si la ruta actual está en la lista de exclusiones
       for (String excludedPath : excludedPaths) {
           // Se usa startsWith para permitir patrones como /resources/**
           if (requestPath.startsWith(request.getContextPath() + excludedPath) || requestPath.equals(request.getContextPath() + excludedPath)) {
               log.debug("Ruta {} excluida del interceptor.", requestPath);
               return true; // Continuar con el procesamiento normal
           }
       }

       // Obtener la sesión
       HttpSession session = request.getSession(false); // false: no crear una nueva sesión si no existe

       // Verificar si el usuario está autenticado (ej. el atributo 'loggedInUser' existe y es válido)
       // Asumimos que un atributo 'loggedInUser' en la sesión indica autenticación.
       boolean isAuthenticated = (session != null && session.getAttribute("loggedInUser") != null);

       if (!isAuthenticated) {
           log.warn("Intento de acceso no autorizado a {}. Redirigiendo a la página de login.", requestPath);
           // Redirigir a la página de inicio de sesión
           response.sendRedirect(request.getContextPath() + "/auth/login");
           return false; // Detener el procesamiento de la solicitud
       }

       log.debug("Usuario autenticado, permitiendo acceso a {}.", requestPath);
       return true; // Permitir que la solicitud continúe
   }

   /**
    * Setter para inyectar la lista de rutas a excluir.
    * @param excludedPaths Lista de patrones de URL a excluir.
    */
   public void setExcludedPaths(List<String> excludedPaths) {
       this.excludedPaths = excludedPaths;
   }
}
	

Con esta configuración y la implementación del interceptor, cualquier solicitud que no coincida con las rutas excluidas será validada por el método preHandle. Si el usuario no está autenticado en la sesión, se le redirigirá automáticamente a la página de inicio de sesión, proporcionando un control de acceso robusto y centralizado.

Etiquetas: Spring MVC interceptor Control de Acceso autenticación Sesión

Publicado el 8-1 07:00