Gestión de cookies

(Enero de 2006)

Introducción

Una de las formas más sencillas de mantener información ante diversas sesiones es el uso de cookies. No voy a explicar qué son, sobreentendemos que todo lector que haya llegado hasta aquí ya sabe lo que son. Ya sabemos que una cookie es un modo muy simple de almacenar información bajo la forma de la vieja y útil estructura "atributo : valor"; donde "atributo" corresponde al nombre de la cookie y "valor" al valor. Por ejemplo, la cookie "aniversario" puede expresarse bajo la forma "aniversario:18-12-2003".

La idea es que una cookie almacena datos definidos por una aplicación servidora (CGI o servlet). Estos datos se guardan en el cliente, concretamente en un archivo que suele contener el nombre del servidor. La ubicación de este archivo dentro del cliente depende de la configuración del sistema y del navegador. Normalmente el archivo está en el directorio Cookies, por ejemplo, en "C:\Documents and Settings\Administrador\Cookies". Nuestro ejemplo incluye dos cookies:

El problema mil veces repetido de las cookies es que hay clientes que inhabilitan su uso, con lo que no nos sirven como mecanismo para guardar información entre sesiones. Sin embargo todavía son bastante utilizadas debido a su sencillez de manejo, normalmente en situaciones donde la información que se quiere almacenar es pequeña, de sencilla estructura y no crucial. Por ejemplo, para guardar las últimas páginas visitadas por el usuario, gustos, idioma, etc. Como ya sabemos una cookie no supone un peligro para la seguridad, aunque si es discutible si supone un peligro para la privacidad. Son pocas las webs que tienen la buena educación de avisar que usan cookies, lo que resulta entendible, ya que todavía hay muchos usuarios que por ignorancia creen que les estan instalando un virús o un troyano.

El servlet crea las cookies en el cliente añadiendo campos a las cabeceras de respuesta. El servlet lee las cookies del cliente leyendo las cabeceras de las peticiones HTTP.

Además del nombre y el valor de la cookie hay otros datos opcionales, como comentario o path. Pero dependiendo de la versión de HTML y sobre todo del navegador se pueden tratar o no. Téngase en cuenta a la hora de hacer aplicaciones compatibles.

Restricciones

Los navegadores pueden soportar cómo mucho aproximadamente 20 cookies por host. Cada cookie no puede superar los 4 Kb. Diferentes servlets dentro de un servidor pueden compartir las cookies, lo que es un medio de intercambiar información. No conviene utilizar caracteres como []()=,""/?@:;

Si creamos una cookie, se debe hacer antes de usar PrintWriter, ya que las cookies se envian al cliente en la cabecera y las cabeceras deben escribirse antes de acceder a PrintWriter. De otro modo, podriamos encontrar problemas con algunos servidores.

Primeros pasos

Nuestro ejemplo

Como ya dijimos hay dos cookies: una guarda los gustos culinarios que el usuario registra en un formulario, la otra simplemente sirve como contador de mis llamadas al servlet.

Este formulario llama al servlet que lee y actualiza las cookies. Las preferencias que teclee a continuación quedarán almacenadas en la cookie "cocina", borrándose el valor anterior.

Platos preferidos: 

El código no hace nada que no se haya explicado. La única partidularidad es que registramos los cambios producidos en las cookies sobre Properties, declaradas en doGet():


      Properties mensajesCocina = new Properties();
      Properties mensajesContador = new Properties();
    

El primer paso es llamar a actualizarCookie():


      actualizarCookie( request, response, mensajesCocina, "cocina", paramGustos );
      actualizarCookie( request, response, mensajesContador, "contador", String.valueOf( visitas) );
    

En este método se escribe en Properties el resultado del intento de lectura de la cookie:


   void actualizarCookie( HttpServletRequest request, HttpServletResponse response,
			  Properties mensajes, String nombreGalleta, String nuevoValor ) {

      //// Obtener cookie
      Cookie galleta = this.getCookie( request, nombreGalleta );

      //// Si no hay cookie, almaceno aviso en 'mensajes'
      if ( galleta == null ) {
	 mensajes.setProperty( "cookie.nombre", "No hay cookie con nombre " + nombreGalleta + ". Se creará");  // Aviso
      }
      //// Si hay cookie, almaceno aviso en 'mensajes'
      else {
	 mensajes.setProperty( "cookie.nombre", galleta.getName());
	 mensajes.setProperty( "cookie.valor.antiguo", galleta.getValue());
      }
    

Al final de actualizarCookie() se crea la nueva cookie con el mismo nombre y el nuevo valor, esto se hace con crearCookie(), que instancia (new) la cookie; terminamos añadiendo la cookie a la cabecera de la respuesta:


      galleta = crearCookie( nombreGalleta, nuevoValor );
      response.addCookie( galleta );
    

Cuando estas tareas concluyen se devuelve el control a doGet(), donde se construye la página. En la página incluimos los datos de Properties:


      //// Escribo en out los mensajes
      escribirMensajes( out, mensajesCocina );
      out.println( "<HR>");
      escribirMensajes( out, mensajesContador );
    

El código completo:


package docen_servlet01;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;
import java.io.*;
import java.net.*;

/******************************************************************************
 * Lee cookies. Si no existieran las crea.
 * En cualquier caso acaba almacenando en las cookies su nuevo valor. En una de las cookies
 * el nuevo valor es la cadena que se pasa como parámetro del formulario HTML. En la otra
 * cookie almaceno un contador de visitas del usuario.
 *
 * El servlet produce una página que informa del resultado del proceso.
 *
 * Ojo: En el entorno JBuilder solo guarda el primer valor, no los sucesivos.
 ******************************************************************************/
public class ControlCookies extends HttpServlet {

   public static final int SECONDS_PER_YEAR = 60*60*24*365; // Segundos de un año (duración de cookie)

   /**********************************************************************************************
    * Método GET: gestiono cookie
    * Almaceno en Properties el resultado de la gestión de cookies
    * Construye una página con dicho resultado.
    **********************************************************************************************/
   public void doGet( HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

      //// Obtengo parametro de formulario HTML
      String paramGustos = request.getParameter( "gustos" );

      //// Guardaremos en Properties los avisos de lo que ha ocurrido con la cookie
      Properties mensajesCocina = new Properties();
      Properties mensajesContador = new Properties();

      //// Nuevo valor de la cookie contador. Si no hay cookie el nuevo valor es 1.
      //// Si hay cookie, incremento el valor.
      Cookie c = this.getCookie( request, "contador" );
      int visitas = 1;
      if ( c != null ) {
	 visitas = Integer.parseInt(c.getValue());
	 visitas++;
      }

      //// Actualizo las cookies
      actualizarCookie( request, response, mensajesCocina, "cocina", paramGustos );
      actualizarCookie( request, response, mensajesContador, "contador", String.valueOf( visitas) );

      response.setContentType("text/html");
      PrintWriter out = response.getWriter();

      //// Escribe el inicio de la página
      out.println("<html>");
      out.println("<head><title>Ejemplo de Servlet</title></head>");
      out.println("<body bgcolor=\"#FFFF9D\"><FONT color=\"#000080\" FACE=\"Arial,Helvetica,Times\" SIZE=2>");
      out.println("<CENTER><H3>Gestionando cookies</H3></CENTER><HR>");

      //// Escribo en out los mensajes
      escribirMensajes( out, mensajesCocina );
      out.println( "<HR>");
      escribirMensajes( out, mensajesContador );

      //// Fin de página
      out.println("</font></body></html>");
   }

   /***********************************************************************************
    * Método POST: reenvio a GET
    **********************************************************************************/
   public void doPost( HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
      doGet(request, response);
   }

   /***********************************************************************************
    * Actualizo la cookie con 'nuevoValor'. En 'mensajes' almaceno el resultado de la
    * operación; nos sirve como repositorio de mensajes.
    * Si no hubiese cookie, la creo de nuevo y almaceno igualmente el nuevo valor.
    **********************************************************************************/
   void actualizarCookie( HttpServletRequest request, HttpServletResponse response,
			  Properties mensajes, String nombreGalleta, String nuevoValor ) {

      //// Obtener cookie
      Cookie galleta = this.getCookie( request, nombreGalleta );

      //// Si no hay cookie, almaceno aviso en 'mensajes'
      if ( galleta == null ) {
	 mensajes.setProperty( "cookie.nombre", "No hay cookie con nombre " + nombreGalleta + ". Se creará");  // Aviso
      }
      //// Si hay cookie, almaceno aviso en 'mensajes'
      else {
	 mensajes.setProperty( "cookie.nombre", galleta.getName());
	 mensajes.setProperty( "cookie.valor.antiguo", galleta.getValue());

	 // No suele funcionar, depende de la version de HTML y de las capacidades del navegador
//	 mensajes.setProperty( "cookie.antigua.path", galleta.getPath());
//	 mensajes.setProperty( "cookie.antigua.dominio", galleta.getDomain());
      }

      //// Creo nueva cookie con el nombre de la anterior y añado la cookie a la cabecera de la respuesta
      galleta = crearCookie( nombreGalleta, nuevoValor );
      response.addCookie( galleta );

      //// Aviso de la cookie nueva o actualizada
      mensajes.setProperty( "cookie.valor.nuevo", nuevoValor );
   }

   /***********************************************************************************
    * Obtengo todas las cookies a partir de la petición (request). Devuelve aquella cuyo
    * nombre es 'cookieName'. Si no la encuentra, devuelve null.
    **********************************************************************************/
   public static Cookie getCookie( HttpServletRequest request, String cookieName ) {
      Cookie[] cookies = request.getCookies();
      for(int i=0; cookies != null && i < cookies.length; i++) {
	 Cookie cookie = cookies[i];
	 if (cookieName.equals(cookie.getName()))
	    return cookie;
      }
      return null;
   }

   /*******************************************************************************
    * Obtiene las cookies de la petición (request). Busca una que coincida con 'cookieName'.
    * Si lo encuentra, devuelve su valor; si no, devuelve 'defaultValue'
    *******************************************************************************/
   public static String getCookieValue( HttpServletRequest request, String cookieName, String defaultValue) {
      Cookie[] cookies = request.getCookies();
      for(int i=0; cookies != null && i < cookies.length; i++) {
	 Cookie cookie = cookies[i];
	 if (cookieName.equals(cookie.getName()))
	    return(cookie.getValue());
      }
      return defaultValue;
   }

   /***********************************************************************************
    * Crea una cookie cuyo nombre y valor son los argumentos. Duración: un año (atributo
    * SECONDS_PER_YEAR).
    **********************************************************************************/
   public static Cookie crearCookie( String nombre, String valor) {
      Cookie c = new Cookie( nombre, valor );
      c.setMaxAge(SECONDS_PER_YEAR);
      return c;
   }

   /***********************************************************************************
    * Escribo en out el contenido de 'mensajes' (claves y valores)
    **********************************************************************************/
   void escribirMensajes( PrintWriter out, Properties mensajes ) {
      //// Recorro los elementos de 'mensajes' y muestro su valor en la página
      Enumeration e = mensajes.propertyNames();
      while ( e.hasMoreElements() ) {
	 String clave = (String) e.nextElement();
	 out.println("" + clave + ": " + mensajes.getProperty( clave ) + "");
      }
   }

}
    

Volver al índice