Gestión de sesiones en Java Las aplicaciones web Servlet son un tema muy interesante. La sesión en Java Servlet se gestiona de diferentes formas, como cookies, la API HttpSession, reescritura de URL, etc. Este es el tercer artículo de la serie de tutoriales de Aplicaciones Web en Java; es posible que desees revisar los dos artículos anteriores también.
Gestión de Sesiones en Java
Este artículo tiene como objetivo explicar la gestión de sesiones en servlets mediante diferentes técnicas y con programas de ejemplo.
-
¿Qué es una sesión?
El protocolo HTTP y los servidores web son stateless, lo que significa que para el servidor web cada solicitud es una nueva solicitud para procesar y no pueden identificar si proviene de un cliente que ha estado enviando solicitudes previamente. Pero a veces en las aplicaciones web, debemos saber quién es el cliente y procesar la solicitud en consecuencia. Por ejemplo, una aplicación de carrito de compras debe saber quién está enviando la solicitud para agregar un artículo y en qué carrito se debe agregar el artículo o quién está enviando la solicitud de pago para poder cobrar el monto al cliente correcto. Una sesión es un estado conversacional entre el cliente y el servidor y puede consistir en múltiples solicitudes y respuestas entre el cliente y el servidor. Dado que tanto HTTP como el servidor web son stateless, la única forma de mantener una sesión es cuando alguna información única sobre la sesión (identificador de sesión) se pasa entre el servidor y el cliente en cada solicitud y respuesta. Hay varias formas de proporcionar un identificador único en la solicitud y respuesta.
-
Autenticación de usuario – Esta es la forma más común en la que el usuario puede proporcionar credenciales de autenticación desde la página de inicio de sesión y luego podemos pasar la información de autenticación entre el servidor y el cliente para mantener la sesión. Este método no es muy efectivo porque no funcionará si el mismo usuario ha iniciado sesión desde diferentes navegadores.
-
Campo oculto de HTML – Podemos crear un campo oculto único en el HTML y cuando el usuario comienza a navegar, podemos establecer su valor único para el usuario y llevar un seguimiento de la sesión. Este método no se puede utilizar con enlaces porque necesita que se envíe el formulario cada vez que se realiza una solicitud del cliente al servidor con el campo oculto. Además, no es seguro porque podemos obtener el valor del campo oculto desde el origen HTML y usarlo para hackear la sesión.
-
Reescritura de URL – Podemos agregar un parámetro identificador de sesión con cada solicitud y respuesta para llevar un seguimiento de la sesión. Esto es muy tedioso porque necesitamos llevar un seguimiento de este parámetro en cada respuesta y asegurarnos de que no esté chocando con otros parámetros.
-
Cookies – Las cookies son pequeñas piezas de información que son enviadas por el servidor web en el encabezado de la respuesta y se almacenan en las cookies del navegador. Cuando el cliente realiza más solicitudes, agrega la cookie al encabezado de la solicitud y podemos utilizarla para llevar un seguimiento de la sesión. Podemos mantener una sesión con cookies, pero si el cliente deshabilita las cookies, entonces no funcionará.
-
API de gestión de sesiones – La API de gestión de sesiones se basa en los métodos anteriores para el seguimiento de sesiones. Algunas de las principales desventajas de todos los métodos anteriores son:
- La mayoría de las veces no queremos solo hacer un seguimiento de la sesión, tenemos que almacenar algunos datos en la sesión que podamos usar en futuras solicitudes. Esto requerirá mucho esfuerzo si intentamos implementarlo.
- Todos los métodos anteriores no son completos en sí mismos, ninguno de ellos funcionará en un escenario particular. Por lo tanto, necesitamos una solución que pueda utilizar estos métodos de seguimiento de sesiones para proporcionar gestión de sesiones en todos los casos.
Por eso necesitamos API de gestión de sesiones y la tecnología de Servlet J2EE viene con una API de gestión de sesiones que podemos usar.
-
-
Gestión de sesiones en Java – Cookies
Las cookies se utilizan mucho en las aplicaciones web para personalizar la respuesta según tu elección o para realizar un seguimiento de la sesión. Antes de avanzar hacia la API de Gestión de Sesiones de Servlet, me gustaría mostrar cómo podemos realizar un seguimiento de la sesión con cookies a través de una pequeña aplicación web. Crearemos una aplicación web dinámica ServletCookieExample con una estructura de proyecto como la imagen a continuación.
El descriptor de implementación web.xml de la aplicación web es:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns="https://java.sun.com/xml/ns/javaee" xsi:schemaLocation="https://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>ServletCookieExample</display-name> <welcome-file-list> <welcome-file>login.html</welcome-file> </welcome-file-list> </web-app>
La página de inicio de nuestra aplicación es login.html donde obtendremos los detalles de autenticación del usuario.
<!DOCTYPE html> <html> <head> <meta charset="US-ASCII"> <title>Página de inicio de sesión</title> </head> <body> <form action="LoginServlet" method="post"> Nombre de usuario: <input type="text" name="user"> <br> Contraseña: <input type="password" name="pwd"> <br> <input type="submit" value="Iniciar sesión"> </form> </body> </html>
Aquí está el LoginServlet que se encarga de la solicitud de inicio de sesión.
package com.journaldev.servlet.session; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * Servlet implementation class LoginServlet */ @WebServlet("/LoginServlet") public class LoginServlet extends HttpServlet { private static final long serialVersionUID = 1L; private final String userID = "Pankaj"; private final String password = "journaldev"; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // get request parameters for userID and password String user = request.getParameter("user"); String pwd = request.getParameter("pwd"); if(userID.equals(user) && password.equals(pwd)){ Cookie loginCookie = new Cookie("user",user); //setting cookie to expiry in 30 mins loginCookie.setMaxAge(30*60); response.addCookie(loginCookie); response.sendRedirect("LoginSuccess.jsp"); }else{ RequestDispatcher rd = getServletContext().getRequestDispatcher("/login.html"); PrintWriter out= response.getWriter(); out.println("<font color=red>El nombre de usuario o la contraseña son incorrectos.</font>"); rd.include(request, response); } } }
Observe la cookie que estamos configurando en la respuesta y luego la estamos reenviando a LoginSuccess.jsp, esta cookie se usará allí para realizar un seguimiento de la sesión. También observe que el tiempo de espera de la cookie se establece en 30 minutos. Idealmente, debería haber una lógica compleja para establecer el valor de la cookie para el seguimiento de sesiones para que no colisione con ninguna otra solicitud.
<%@ page language="java" contentType="text/html; charset=US-ASCII" pageEncoding="US-ASCII"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "https://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=US-ASCII"> <title>Página de inicio de sesión exitosa</title> </head> <body> <% String userName = null; Cookie[] cookies = request.getCookies(); if(cookies !=null){ for(Cookie cookie : cookies){ if(cookie.getName().equals("user")) userName = cookie.getValue(); } } if(userName == null) response.sendRedirect("login.html"); %> <h3>¡Hola <%=userName %>, sesión iniciada correctamente.</h3> <br> <form action="LogoutServlet" method="post"> <input type="submit" value="Cerrar sesión" > </form> </body> </html>
Observe que si intentamos acceder directamente al JSP, nos reenviará a la página de inicio de sesión. Cuando hagamos clic en el botón de Cerrar sesión, debemos asegurarnos de que la cookie se elimine del navegador del cliente.
package com.journaldev.servlet.session; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; /** * Servlet implementation class LogoutServlet */ @WebServlet("/LogoutServlet") public class LogoutServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); Cookie loginCookie = null; Cookie[] cookies = request.getCookies(); if(cookies != null){ for(Cookie cookie : cookies){ if(cookie.getName().equals("user")){ loginCookie = cookie; break; } } } if(loginCookie != null){ loginCookie.setMaxAge(0); response.addCookie(loginCookie); } response.sendRedirect("login.html"); } }
No hay un método para eliminar la cookie,
-
Sesión en Java Servlet – HttpSession
La API de Servlet proporciona gestión de sesiones a través de la interfaz
HttpSession
. Podemos obtener la sesión desde el objeto HttpServletRequest utilizando los siguientes métodos. HttpSession nos permite establecer objetos como atributos que pueden ser recuperados en futuras solicitudes.- HttpSession getSession() – Este método siempre devuelve un objeto HttpSession. Retorna el objeto de sesión adjunto con la solicitud; si la solicitud no tiene una sesión adjunta, entonces crea una nueva sesión y la devuelve.
- HttpSession getSession(boolean flag) – Este método devuelve un objeto HttpSession si la solicitud tiene sesión, de lo contrario devuelve nulo.
Algunos de los métodos importantes de HttpSession son:
- String getId() – Devuelve una cadena que contiene el identificador único asignado a esta sesión.
- Object getAttribute(String name) – Devuelve el objeto vinculado con el nombre especificado en esta sesión, o nulo si no hay ningún objeto vinculado bajo ese nombre. Algunos otros métodos para trabajar con atributos de sesión son
getAttributeNames()
,removeAttribute(String name)
ysetAttribute(String name, Object value)
. - long getCreationTime() – Devuelve el tiempo en que se creó esta sesión, medido en milisegundos desde la medianoche del 1 de enero de 1970 GMT. Podemos obtener el tiempo de último acceso con el método
getLastAccessedTime()
. - setMaxInactiveInterval(int interval) – Especifica el tiempo, en segundos, entre las solicitudes del cliente antes de que el contenedor de servlet invalide esta sesión. Podemos obtener el valor de tiempo de espera de sesión del método
getMaxInactiveInterval()
. - ServletContext getServletContext() – Devuelve el objeto ServletContext para la aplicación.
- boolean isNew() – Devuelve true si el cliente aún no conoce la sesión o si el cliente elige no unirse a la sesión.
- void invalidate() – Invalida esta sesión y luego desvincula cualquier objeto vinculado a ella.
Entendiendo la Cookie JSESSIONID
Cuando usamos el método HttpServletRequest getSession() y crea una nueva solicitud, crea el nuevo objeto HttpSession y también agrega una Cookie al objeto de respuesta con el nombre JSESSIONID y el valor como ID de sesión. Esta cookie se utiliza para identificar el objeto HttpSession en futuras solicitudes del cliente. Si las cookies están deshabilitadas en el lado del cliente y estamos utilizando la reescritura de URL, entonces este método utiliza el valor jsessionid de la URL de la solicitud para encontrar la sesión correspondiente. La cookie JSESSIONID se utiliza para el seguimiento de sesión, por lo que no deberíamos usarla para nuestros propósitos de aplicación para evitar problemas relacionados con la sesión. Veamos un ejemplo de gestión de sesión utilizando el objeto HttpSession. Crearemos un proyecto web dinámico en Eclipse con el contexto de servlet como ServletHttpSessionExample. La estructura del proyecto se verá como en la imagen de abajo.
login.html es igual que en el ejemplo anterior y se define como página de bienvenida para la aplicación en web.xml El servlet LoginServlet creará la sesión y establecerá atributos que podemos usar en otros recursos o en futuras solicitudes.
package com.journaldev.servlet.session; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; /** * Implementación del Servlet LoginServlet */ @WebServlet("/LoginServlet") public class LoginServlet extends HttpServlet { private static final long serialVersionUID = 1L; private final String userID = "admin"; private final String password = "password"; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // obtener parámetros de solicitud para usuario y contraseña String user = request.getParameter("user"); String pwd = request.getParameter("pwd"); if(userID.equals(user) && password.equals(pwd)){ HttpSession session = request.getSession(); session.setAttribute("user", "Pankaj"); // estableciendo expiración de sesión en 30 minutos session.setMaxInactiveInterval(30*60); Cookie userName = new Cookie("user", user); userName.setMaxAge(30*60); response.addCookie(userName); response.sendRedirect("LoginSuccess.jsp"); }else{ RequestDispatcher rd = getServletContext().getRequestDispatcher("/login.html"); PrintWriter out= response.getWriter(); out.println("<font color=red>Ya sea el nombre de usuario o la contraseña están incorrectos.</font>"); rd.include(request, response); } } }
Nuestro código LoginSuccess.jsp se muestra a continuación.
<%@ page language="java" contentType="text/html; charset=US-ASCII"
pageEncoding="US-ASCII"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "https://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
<title>Página de Éxito de Inicio de Sesión</title>
</head>
<body>
<%
// permitir acceso solo si existe la sesión
String user = null;
if(session.getAttribute("user") == null){
response.sendRedirect("login.html");
}else user = (String) session.getAttribute("user");
String userName = null;
String sessionID = null;
Cookie[] cookies = request.getCookies();
if(cookies !=null){
for(Cookie cookie : cookies){
if(cookie.getName().equals("user")) userName = cookie.getValue();
if -
Gestión de Sesiones en Java Servlet – Reescritura de URL
Como vimos en la última sección, podemos gestionar una sesión con HttpSession, pero si desactivamos las cookies en el navegador, no funcionará porque el servidor no recibirá la cookie JSESSIONID del cliente. La API de Servlet proporciona soporte para la reescritura de URL que podemos usar para gestionar la sesión en este caso. La mejor parte es que desde el punto de vista del código, es muy fácil de usar e implica un paso: codificar la URL. Otra cosa buena de la codificación de URL del Servlet es que es un enfoque de respaldo y se activa solo si las cookies del navegador están desactivadas. Podemos codificar la URL con el método
encodeURL()
de HttpServletResponse y si tenemos que redirigir la solicitud a otro recurso y queremos proporcionar información de sesión, podemos usar el métodoencodeRedirectURL()
. Crearemos un proyecto similar al anterior excepto que usaremos métodos de reescritura de URL para asegurarnos de que la gestión de sesión funcione bien incluso si las cookies están desactivadas en el navegador. La estructura del proyecto ServletSessionURLRewriting en eclipse se ve como la imagen a continuación.paquete com.journaldev.servlet.session; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; /** * Implementación del Servlet de inicio de sesión */ @WebServlet("/LoginServlet") public class LoginServlet extends HttpServlet { private static final long serialVersionUID = 1L; private final String userID = "admin"; private final String password = "password"; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // obtener parámetros de solicitud para usuario y contraseña String usuario = request.getParameter("usuario"); String contraseña = request.getParameter("contraseña"); if(userID.equals(usuario) && password.equals(contraseña)){ HttpSession session = request.getSession(); session.setAttribute("usuario", "Pankaj"); // establecer expiración de sesión en 30 minutos session.setMaxInactiveInterval(30*60); Cookie userName = new Cookie("usuario", usuario); response.addCookie(userName); // Obtener la cadena de URL codificada String encodedURL = response.encodeRedirectURL("LoginSuccess.jsp"); response.sendRedirect(encodedURL); }else{ RequestDispatcher rd = getServletContext().getRequestDispatcher("/login.html"); PrintWriter out= response.getWriter(); out.println("<font color=red>Nombre de usuario o contraseña incorrectos.</font>"); rd.include(request, response); } } }
<%@ page language="java" contentType="text/html; charset=US-ASCII" pageEncoding="US-ASCII"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "https://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=US-ASCII"> <title>Página de éxito de inicio de sesión</title> </head> <body> <% //permitir acceso solo si existe la sesión String usuario = null; if(session.getAttribute("usuario") == null){ response.sendRedirect("login.html"); }else usuario = (String) session.getAttribute("usuario"); String nombreUsuario = null; String sessionID = null; Cookie[] cookies = request.getCookies(); if(cookies !=null){ for(Cookie cookie : cookies){ if(cookie.getName().equals("usuario")) nombreUsuario = cookie.getValue(); if(cookie.getName().equals("JSESSIONID")) sessionID = cookie.getValue(); } }else{ sessionID = session.getId(); } %> <h3>¡Hola <%=nombreUsuario %>, inicio de sesión exitoso. Su ID de sesión=<%=sessionID %></h3> <br> Usuario=<%=usuario %> <br> <!-- necesitamos codificar todas las URL donde queremos que se pase la información de la sesión --> <a href="<%=response.encodeURL("CheckoutPage.jsp") %>">Página de pago</a> <form action="<%=response.encodeURL("LogoutServlet") %>" method="post"> <input type="submit" value="Cerrar sesión" > </form> </body> </html>
<%@ page language="java" contentType="text/html; charset=US-ASCII" pageEncoding="US-ASCII"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "https://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=US-ASCII"> <title>Página de éxito de inicio de sesión</title> </head> <body> <% String nombreUsuario = null; //permitir acceso solo si existe la sesión if(session.getAttribute("usuario") == null){ response.sendRedirect("login.html"); }else nombreUsuario = (String) session.getAttribute("usuario"); String sessionID = null; Cookie[] cookies = request.getCookies(); if(cookies !=null){ for(Cookie cookie : cookies){ if(cookie.getName().equals("usuario")) nombreUsuario = cookie.getValue(); } } %> <h3>¡Hola <%=nombreUsuario %>, realiza la compra.</h3> <br> <form action="<%=response.encodeURL("LogoutServlet") %>" method="post"> <input type="submit" value="Cerrar sesión" > </form> </body> </html>
paquete com.journaldev.servlet.session;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;/**
* Implementación del Servlet de cierre de sesión
*/
@WebServlet("/LogoutServlet")
public class LogoutServlet extends HttpServlet {
private static final long serialVersionUID =
Eso es todo para la gestión de sesiones en los servlets de Java, veremos los Filtros y Escuchadores de Servlets y las Cookies en futuros artículos. Actualización: Consulte el próximo artículo en la serie Filtro de Servlet.