Básico
Mantener el estado en una aplicación web
Sessions y Cookie
Navegador, Servidor, Cookies y HTTP
Una manera de mantener el estado de una conexion en el protocolo HTTP
1.1 se consigue mediante el envio de IDs de sesion entre el navegador y
el servidor al que conecta. De esta manera el navegador conserva
dicho ID para futuras conexiones. Se trata de un mecanismo, mediante el
que se
envía reciben un conjunto arbitrario de datos en conexiones HTTP a
través de la cabecera de respuesta "Set-cookie", y que el cliente
almacena localmente y re-envía en cada nueva conexion al servidor a
través de la cabecera "Cookie" de la request. Entre los atributos que
contiene una Cookie, puede ser casi cualquier dato string, establecido
por el servidor, solemos encontrar el nombre del dominio, una ruta
dentro de dicho servidor,y un tiempo de expiración o duración máxima, un
flag que indica si se trata de una cookie de seguridad y un flag para
indicar si se trata de HTTP-only.
Cada vez que el navegador realiza una petición, busca en su almacen
de cookies que se corresponden con el nombre de dominio y la ruta y
envía la cookie junto con los datos de la request. El atributo Expires
nos da un valor absoluto de fecha de caducidad mientras que Max-Age,
ambos mutuamente exclusivos, indica el numero de segundos que han de
pasar hasta que la cookie expire. Si el navegador al leer la
Cookie comprueba que ha caducado
la borra automaticamente, por otro lado si dicha cookie no contiene
alguno de estos atributos, se borra cuando cerramos el navegador.
Una cookie puede contener el atributo Secure, entonces, el navegador
la enviará unicamente a través del protocolo HTTPS, lo cual
asegura que se enviara cifrada. El atributo HTTP-only restringe el
envío a peticiones de navegador (solo a través de la request GET-POST),
otros plugins no tendrian acceso a enviar dicha cookie. Servidores Web y
de aplicaciones, usan las cookies para almacenar el estado de las
conexiones cliente (session). En los servidores de aplicaciones JEE, el
nombre por defecto es JSESSIONID. Si analizamos las cabeceras de
request-response
encontrariamos algo parecido a lo siguiente:
Request 1
GET /users/home HTTP/1.1
Host: www.servidor.com
Response 1
HTTP/1.1 302 Moved Temporarily
Location: https://www.servidor.com/access/login
Set-Cookie: JSESSIONID=NRxclGg2vG7MdlLn; Domain=.servidor.com; Path=/; HttpOnly
Request 2
GET /access/login HTTP/1.1
Host: www.servidor.com
Cookie: JSESSIONID=NRxclGg2vG7MdlLn
Response 2
HTTP/1.1 200 OK
Content-Type: text/html;charset=UTF-8
Content-Length: 21765
Request 3
POST /access/login HTTP/1.1
Host: www.servidor.com
Cookie: JSESSIONID=NRxclGg2vG7MdlLn
Response 3
HTTP/1.1 302 Moved Temporarily
Location: http://www.servidor.com/users/home
Set-Cookie: username=Jose; Expires=Wed, 02-Jun-2021 12:15:47 GMT;
Domain=.servidor.com; Path=/; HttpOnly
Request 4
GET /users/home HTTP/1.1
Host: www.servidor.com
Cookie: JSESSIONID=NRxclGg2vG7MdlLn; username=Jose
Response 4
HTTP/1.1 200 OK
Content-Type: text/html;charset=UTF-8
Content-Length: 2653380
De la misma forma que las cabeceras Set-Cookie en la respuesta se
usan para enviar cookies a los clientes en response, de manera que se
almacenen en el cliente si este lo permite; las cabeceras Cookie en la
peticion se usan para enviarlas de vuelta al servidor. En el caso
imaginario descrito arriba, el usuario intenta acceder a la direccion
/users/home y se le redirige a la pagina de login. Cuando accede a
pagina el servidor incluye un ID de session,
mediante Set-Cookie. Cada vez que el navegador conecta con el servidor, se incluye la cookie JSESSIONID.
Una vez que el usuario se autentifica, el servidor reenvia una cookie con la clave username.
Uno de los problemas que presenta el uso de cookies es que los
usuarios pueden desactivar que el navegador las acepte, de esta manera
impedir que se envie el ID de sesion, la manera de pasar por alto esta
situacione, es incluirla en la URL. Se trata de una forma comun
de transmitir la sesion. Una aplicación web o un servidor de
aplicaciones, busca un patron reconocible en la uri y extrae de ahí los
datos
de la sesion. Un ejemplo en PHP:
http://www.servidor.com/usuarios?PHPSESSID=NRxclGg2vG7kI4MdlLn&foo=bar&lang=es
Las aplicaciones JEE usan una tecnica distinta. El session ID se coloca en la última parte de la URI, lo que
no sobrecarga query string, de esta manera no hay conflictos con otros parametros de la request.
http://www.servidor.com/usuarios;JSESSIONID=NRxclGg2vG7kI4MdlLn?foo=bar&lang=es
Cualquiera de las tecnicas usadas el resultado es el mísmo: Incluir el
session ID en la URL para evitar la necesidad de almacenar Cookies,
Deberíamos preguntarnos, de que manera entonces como recoge el navegador el session ID la primera vez,
Una request URL es unicamente para convenir el session ID desde el navegador al servidor al que conecta.
De donde se obtiene entonces la primera vez? Dicho session
ID se incluye en cada URL que la aplicacion envia su response,
incluidos los enlaces, actions y redirects.
Para incluir session ID en cada URL, HttpServletResponse
contiene dos metodos que incluyen el ID de session si es necesario: :
encodeURL y encodeRedirectURL . Para cada salida de la
response que escriba
una URL, se usa el metodo encodeURL el cual devuelve la URL
correcta. De la misma manera una URL para una response
de tipo sendRedirect, llamariamos al metodo encodeRedirectURL, de
manera que se incluya el JSESSIONID, esto sucedera si se da alguno
de los siguientes casos:
Una session activa en la request en curso. (Bien se realizo una peticion que contenía session ID o bien la
aplicacion creo uno nuevo).
No contenia la cookie JSESSIONID.
La URL es relativa a la aplicacion.
El descriptor de la aplicacion tiene configurado URL rewriting.
El caso segundo es el mas problematico. La unica forma de detectar si un navegador acepta cookies, es enviar una y comprobar
si es devuelta en la siguiente peticion. Sin embargo, es necesario crear
una session para asociar una request con otra; sino, como
podemos saber si la request era la primera que envia otro usuario o es una segunda request del mismo? Asi que de esta manera,
se asume que cuando no exista JSESSIONID en la request, esto nos indica
que el navegador no acepta cookies. Lo cual no tiene por que
ser siempre cierto.