session - español - rfc sip call flows



Sesión o confusión de cookies (2)

He visto en algunos sitios web que el usuario inició sesión en sus cuentas y luego cerró el navegador.

Después de cerrar y volver a abrir el navegador y sus cuentas todavía están registrados.

Pero algunos sitios web, no pueden hacer eso.

Estoy confundido de que se considera sesión o cookie?

Si quiero que mi sitio web cookie.setMaxAge() así, ¿tengo que configurar session.setMaxInactiveInterval() o cookie.setMaxAge() ?


La respuesta correcta tiene muchos defectos, ver mi comentario allí. El asunto es en realidad más fácil. Necesitará un almacén de datos persistente (como una base de datos SQL). También puede utilizar ServletContext , pero se cerrará la sesión del usuario después de reiniciar el servidor o de volver a implementar la aplicación. No se olvide de sincronizar correctamente, si utiliza un HashMap en ServletContext , ya que puede accederse al mismo tiempo desde más subprocesos.

No hackee con la sesión del servidor y su ID, no está bajo su control y algunos servidores cambian el ID de sesión si aparece una solicitud con JSESSIONID después de que el servidor haya caducado la sesión original. Enrolla tu propia galleta.

Básicamente necesitas:

  • cookie propia, que no es persistente, con un valor aleatorio seguro
  • un almacén de datos
  • un javax.servlet.Filter para comprobar el inicio de sesión

La implementación del filtro podría verse así:

public class LoginFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, 
            FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;

        // Java 1.8 stream API used here
        Cookie loginCookie = Arrays.stream(req.getCookies()).filter(c -> c.getName()
                .equals("MY_SESSION_COOKIE")).findAny().orElse(null);

        // if we don't have the user already in session, check our cookie MY_SESSION_COOKIE
        if (req.getSession().getAttribute("currentUser") == null) {
            // if the cookie is not present, add it
            if (loginCookie == null) {
                loginCookie = new Cookie("MY_SESSION_COOKIE", UUID.randomUUID().toString());
                // Store that cookie only for our app. You can store it under "/", 
                // if you wish to cover all webapps on the server, but the same datastore
                // needs to be available for all webapps.
                loginCookie.setPath(req.getContextPath());
                loginCookie.setMaxAge(DAYS.toSeconds(1)); // valid for one day, choose your value
                resp.addCookie(loginCookie);
            }
            // if we have our cookie, check it
            else {
                String userId = datastore.getLoggedUserForToken(loginCookie.getValue());
                // the datastore returned null, if it does not know the token, or 
                // if the token is expired
                req.getSession().setAttribute("currentUser", userId);
            }
        }
        else {
            if (loginCookie != null)
                datastore.updateTokenLastActivity(loginCookie.getValue());
        }

        // if we still don't have the userId, forward to login
        if (req.getSession().getAttribute("currentUser") == null)
            resp.sendRedirect("login.jsp");
        // else return the requested resource
        else
            chain.doFilter(request, response);
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void destroy() {
    }

}

Después de que el usuario inicie sesión, debe agregar el valor de MY_SEESSION_COOKIE al almacén de datos junto con el userId y eliminarlo al cerrar la sesión. También debe almacenar la fecha de caducidad en el almacén de datos y verificarla antes de aceptar el token, no debe confiar en que el navegador respete la propiedad maxAge.

Y no se olvide de agregar un poco de limpieza del almacén de datos para evitar que las cookies sobresalientes queden para siempre.

El código anterior no se probó en la vida real, puede haber algunas peculiaridades, pero la idea básica debería funcionar. Es al menos mucho mejor que la solución aceptada.


* Esta respuesta tiene serios defectos, ver comentarios. *

Su pregunta es sobre el seguimiento de la sesión .

[PARTE 1]: OBJETO DE SESIÓN

La solicitud HTTP se procesa por separado, por lo que para mantener la información entre cada solicitud (por ejemplo, información sobre el usuario), se debe crear un objeto de sesión en el lado del servidor.

Algunos sitios web no necesitan una sesión en absoluto. Un sitio web donde los usuarios no pueden modificar ningún contenido no tendrá que administrar una sesión (por ejemplo, un CV en línea). No necesitará ninguna cookie o sesión en dicho sitio web.

Crear una sesión:

En un servlet, use el método request.getSession(true) del objeto HttpServletRequest para crear un nuevo objeto HttpSession. Tenga en cuenta que si utiliza request.getSession(false) , se devolverá nulo si la sesión aún no se ha creado. Mira esta respuesta para más detalles .

Establecer / Obtener atributos:

El propósito de una sesión es mantener la información en el lado del servidor entre cada solicitud. Por ejemplo, manteniendo el nombre del usuario:

session.setAttribute("name","MAGLEFF");
// Cast
String name = (String) session.getAttribute("name");

Destruye una sesión:

Una sesión se destruirá automáticamente si se mantiene inactiva durante demasiado tiempo. Mira esta respuesta para más detalles . Pero puede forzar manualmente la destrucción de la sesión, en el caso de una acción de cierre de sesión, por ejemplo:

HttpSession session = request.getSession(true); 
session.invalidate();

[PARTE 2]: Entonces ... únete al lado oscuro, ¿tenemos COOKIES?

Aquí vienen las galletas.

JSESSIONID:

Se crea una cookie JSESSIONID en la computadora del usuario cada vez que se crea una sesión con request.getSession() . ¿Por qué? Porque cada sesión creada en el lado del servidor tiene una identificación. No puede acceder a la sesión de otro usuario, a menos que no tenga la ID correcta. Esta ID se guarda en la cookie JSESSIONID y permite al usuario encontrar su información. ¡Mira esta respuesta para más detalles !

¿Cuándo se elimina un JSESSIONID?

JSESSIONID no tiene fecha de caducidad: es una cookie de sesión . Como todas las cookies de sesión, se eliminarán cuando se cierre el navegador. Si utiliza el mecanismo básico de JSESSIONID, la sesión se volverá inaccesible después de cerrar y volver a abrir el navegador, ya que se elimina la cookie JSESSIONID.

Tenga en cuenta que el cliente no puede acceder a la sesión, pero aún se está ejecutando en el lado del servidor. La configuración de MaxInactiveInterval permite que el servidor invalide automáticamente la sesión cuando ha estado inactivo durante demasiado tiempo.

Destrucción malvada de JSESSIONID

Solo por diversión, un día encontré este código en un proyecto. Se usó para invalidar la sesión al eliminar la cookie JSESSIONID con javascript:

<SCRIPT language="JavaScript" type="text/javascript">

    function delete_cookie( check_name ) {
        // first we'll split this cookie up into name/value pairs
        // note: document.cookie only returns name=value, not the other components
        var a_all_cookies = document.cookie.split( ';' );
        var a_temp_cookie = '';
        var cookie_name = '';
        var cookie_value = '';
        var b_cookie_found = false; // set boolean t/f default f
        // var check_name = 'JSESSIONID';
        var path = null;

        for ( i = 0; i < a_all_cookies.length; i++ )
        {
            // now we'll split apart each name=value pair
            a_temp_cookie = a_all_cookies[i].split( '=' );
            // and trim left/right whitespace while we're at it
            cookie_name = a_temp_cookie[0].replace(/^\s+|\s+$/g, '');
            // alert (cookie_name);

            // if the extracted name matches passed check_name
            if ( cookie_name.indexOf(check_name) > -1 )
            {
                b_cookie_found = true;
                // we need to handle case where cookie has no value but exists (no = sign, that is):
                if ( a_temp_cookie.length > 1 )
                {
                    cookie_value = unescape( a_temp_cookie[1].replace(/^\s+|\s+$/g, '') );
                    document.cookie = cookie_name + "=" + cookie_value +
                    ";path=/" +
                    ";expires=Thu, 01-Jan-1970 00:00:01 GMT";
                    // alert("cookie deleted " + cookie_name);
                }
            }
            a_temp_cookie = null;
            cookie_name = '';
        }
        return true;
    }
    // DESTROY
    delete_cookie("JSESSIONID");

</SCRIPT>

Dale otra mirada a esta respuesta . Con JavaScript, JSESSIONID puede leerse, modificarse, tener su sesión perdida o secuestrada.

[PARTE 3]: MANTENER UNA SESIÓN DESPUÉS DE CERRAR SU NAVEGADOR

Después de cerrar y volver a abrir el navegador y sus cuentas aún están registradas. Pero algunos sitios web no pueden hacerlo así. Estoy confundido de que se considera sesión o cookie?

Es una galleta

Vimos que cuando el navegador web ha eliminado la cookie de sesión JSESSIONID, el objeto de sesión en el lado del servidor se pierde. No hay manera de acceder de nuevo sin la ID correcta.

Si quiero que mi sitio web inicie sesión así, ¿tengo que configurar session.setMaxInactiveInterval () o cookie.setMaxAge ()?

También vimos que session.setMaxInactiveInterval() era para evitar ejecutar una sesión perdida indefinidamente. JSESSIONID cookie cookie.setMaxAge() tampoco nos llevará a ningún lado.

Utilice una cookie persistente con el ID de sesión:

Llegué a esta solución después de leer los siguientes temas:

La idea principal es registrar la sesión del usuario en un mapa, colocada en el contexto del servlet. Cada vez que se crea una sesión, se agrega al Mapa con el valor JSESSIONID para la clave; También se crea una cookie persistente para memorizar el valor JSESSIONID, con el fin de encontrar la sesión después de que la cookie JSESSIONID haya sido destruida.

Al cerrar el navegador web, JSESSIONID se destruye. Pero todos los objetos de HttpSession se han mantenido en un Mapa en el lado del servidor, y puede acceder a la sesión correcta con el valor guardado en la cookie persistente.

Primero, agregue dos escuchas en su descriptor de implementación web.xml.

<listener>
    <listener-class>
        fr.hbonjour.strutsapp.listeners.CustomServletContextListener
    </listener-class>
</listener>

<listener>
    <listener-class>
        fr.hbonjour.strutsapp.listeners.CustomHttpSessionListener
    </listener-class>
</listener>

El CustomServletContextListener crea un mapa en la inicialización del contexto. Este mapa registrará todas las sesiones creadas por el usuario en esta aplicación.

/**
 * Instanciates a HashMap for holding references to session objects, and
 * binds it to context scope.
 * Also instanciates the mock database (UserDB) and binds it to 
 * context scope.
 * @author Ben Souther; [email protected]
 * @since Sun May  8 18:57:10 EDT 2005
 */
public class CustomServletContextListener implements ServletContextListener{

    public void contextInitialized(ServletContextEvent event){
        ServletContext context = event.getServletContext();

        //
        // instanciate a map to store references to all the active
        // sessions and bind it to context scope.
        //
        HashMap activeUsers = new HashMap();
        context.setAttribute("activeUsers", activeUsers);
    }

    /**
     * Needed for the ServletContextListener interface.
     */
    public void contextDestroyed(ServletContextEvent event){
        // To overcome the problem with losing the session references
        // during server restarts, put code here to serialize the
        // activeUsers HashMap.  Then put code in the contextInitialized
        // method that reads and reloads it if it exists...
    }
}

CustomHttpSessionListener colocará la sesión en el mapa activeUsers cuando se cree.

/**
 * Listens for session events and adds or removes references to 
 * to the context scoped HashMap accordingly.
 * @author Ben Souther; [email protected]
 * @since Sun May  8 18:57:10 EDT 2005
 */
public class CustomHttpSessionListener implements HttpSessionListener{

    public void init(ServletConfig config){
    }

    /**
     * Adds sessions to the context scoped HashMap when they begin.
     */
    public void sessionCreated(HttpSessionEvent event){
        HttpSession    session = event.getSession();
        ServletContext context = session.getServletContext();
        HashMap<String, HttpSession> activeUsers =  (HashMap<String, HttpSession>) context.getAttribute("activeUsers");

        activeUsers.put(session.getId(), session);
        context.setAttribute("activeUsers", activeUsers);
    }

    /**
     * Removes sessions from the context scoped HashMap when they expire
     * or are invalidated.
     */
    public void sessionDestroyed(HttpSessionEvent event){
        HttpSession    session = event.getSession();
        ServletContext context = session.getServletContext();
        HashMap<String, HttpSession> activeUsers = (HashMap<String, HttpSession>)context.getAttribute("activeUsers");
        activeUsers.remove(session.getId());
    }

}

Utilice un formulario básico para probar la autenticación de un usuario por nombre / contraseña. Este formulario de login.jsp es solo para prueba.

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
        <title><bean:message key="formulaire1Title" /></title>
    </head>
    <body>
        <form action="login.go" method="get">
            <input type="text" name="username" />
            <input type="password" name="password" />
            <input type="submit" />
        </form>
    </body>
</html>

Aquí vamos. Este java servlet se reenvía a una página de inicio de sesión cuando el usuario no está en sesión, y a otra página cuando está. Sólo está destinado a probar la sesión persistente!

public class Servlet2 extends AbstractServlet {

    @Override
    protected void doGet(HttpServletRequest pRequest,
            HttpServletResponse pResponse) throws IOException, ServletException {
        String username = (String) pRequest.getParameter("username");
        String password = (String) pRequest.getParameter("password");
        // Session Object
        HttpSession l_session = null;

        String l_sessionCookieId = getCookieValue(pRequest, "JSESSIONID");
        String l_persistentCookieId = getCookieValue(pRequest, "MY_SESSION_COOKIE");

        // If a session cookie has been created
        if (l_sessionCookieId != null)
        {
            // If there isn't already a persistent session cookie
            if (l_persistentCookieId == null)
            {
                addCookie(pResponse, "MY_SESSION_COOKIE", l_sessionCookieId, 1800);
            }
        }
        // If a persistent session cookie has been created
        if (l_persistentCookieId != null)
        {
            HashMap<String, HttpSession> l_activeUsers = (HashMap<String, HttpSession>) pRequest.getServletContext().getAttribute("activeUsers");
            // Get the existing session
            l_session = l_activeUsers.get(l_persistentCookieId);
        }
        // Otherwise a session has not been created
        if (l_session == null)
        {
                    // Create a new session
            l_session = pRequest.getSession();
        }

            //If the user info is in session, move forward to another page
        String forward = "/pages/displayUserInfo.jsp";

        //Get the user
        User user = (User) l_session.getAttribute("user");

        //If there's no user
        if (user == null)
        {
                    // Put the user in session
            if (username != null && password != null)
            {
                l_session.setAttribute("user", new User(username, password));
            }
                    // Ask again for proper login
            else
            {
                forward = "/pages/login.jsp";
            }
        }
        //Forward
        this.getServletContext().getRequestDispatcher(forward).forward( pRequest, pResponse );

    }

La cookie MY_SESSION_COOKIE guardará el valor de la cookie JSESSIONID. Cuando se destruye la cookie JSESSIONID, MY_SESSION_COOKIE sigue ahí con el ID de sesión.

JSESSIONID se ha ido con la sesión del navegador web, pero elegimos usar una cookie persistente y simple, junto con un mapa de todas las sesiones activas colocadas en el contexto de la aplicación. La cookie persistente nos permite encontrar la sesión correcta en el mapa.

No olvide estos métodos útiles creados por BalusC para agregar / obtener / eliminar cookies:

/**
 * 
 * @author BalusC
 */
public static String getCookieValue(HttpServletRequest request, String name) {
    Cookie[] cookies = request.getCookies();
    if (cookies != null) {
        for (Cookie cookie : cookies) {
            if (name.equals(cookie.getName())) {
                return cookie.getValue();
            }
        }
    }
    return null;
}

/**
 * 
 * @author BalusC
 */
public static void addCookie(HttpServletResponse response, String name, String value, int maxAge) {
    Cookie cookie = new Cookie(name, value);
    cookie.setPath("/");
    cookie.setMaxAge(maxAge);
    response.addCookie(cookie);
}

/**
 * 
 * @author BalusC
 */
public static void removeCookie(HttpServletResponse response, String name) {
    addCookie(response, name, null, 0);
}

}

La última solución se probó con glassfish en localhost, con chrome para el navegador web, en windows. Solo depende de una sola cookie, y no necesita una base de datos. Pero en realidad, no sé cuáles son los límites de tal mecanismo. Solo pasé la noche llegando a esta solución, sin saber si será buena o mala.

GRACIAS

Todavía estoy aprendiendo, por favor dígame si hay algún error en mi respuesta. Gracias, @ +





cookies