2012-05-30 8 views
6

Tenemos una aplicación web que usa Jetty 8.1, dojo y cometd que interactúa entre el navegador y el contenedor web utilizando (1) una API REST JSON/HTTP para operaciones síncronas y (2) una API cometd para recibir numerosos eventos desde el servidor.Vinculación de la sesión cometd con la sesión HTTP

Lo que no estamos del todo claros es cómo administrar elegantemente las sesiones de autenticación de estas dos API diferentes, especialmente desde que cometd para nosotros utilizará websocket en lugar de HTTP normal siempre que sea posible. La aplicación utiliza autenticación basada en formularios utilizando un módulo Jetty LDAP estándar. Por lo que desde el punto de vista de HTTP del contenedor proporciona el navegador con un jsessionid estándar, que se ve así:

Cookie: jsessionid=758E2FAD7C199D722DA8B5E243E0E27D 

Basado en el poste de Simone Bordet here parece que la solución recomendada es pasar este símbolo durante el apretón de manos cometd que es lo que estás haciendo.

El problema que tenemos es que hay dos sesiones fundamentalmente diferentes: la sesión HTTP y la sesión comeda de Bayeux. Por razones tales como posibles pérdidas de memoria y problemas de seguridad, deseamos que finalicen al unísono o que se "emparejen". Si la sesión HTTP de un usuario finaliza, queremos que la sesión de Bayeux correspondiente finalice también y viceversa. ¿Hay una manera recomendada de hacer esto?

Respuesta

11

La sesión HTTP y las sesiones CometD tienen diferentes ciclos de vida: por ejemplo, si hay un error de conexión temporal, la sesión CometD fallará, y el servidor solicitará al cliente un nuevo intercambio, creando así un CometD diferente sesión (que representa al mismo usuario, pero con un CometD diferente clientId). En el mismo caso, el HttpSession seguirá siendo el mismo.

Teniendo esto en cuenta, debe mantener, a nivel de aplicación, una asignación entre un nombre de usuario, el corresponsal HttpSession, y el corresponsal ServerSession. Llamemos a este mapeo HttpCometDMapper. Cada vez que un nuevo usuario inicia sesión, usted registra su nombre (u otro identificador único del usuario), el HttpSession, y el actual ServerSession. Probablemente necesite un proceso de dos pasos, donde primero vincula el nombre de usuario y el HttpSession, y luego el mismo nombre de usuario con el ServerSession.

Si se realiza un nuevo apretón de manos CometD, actualice el asignador con el nuevo ServerSession.

puede vincular las dos sesiones al registrar un HttpSessionListener a la HttpSession de modo que cuando se destruye, se recupera la corriente CometD ServerSession desde el asignador y llamar ServerSession.disconnect() en él.

El viceversa es un poco más complicado porque CometD no tiene un concepto de tiempo de inactividad como HttpSession. Debe implementarse en la aplicación con su propia lógica.

Una parte de hacerlo es registrar un RemoveListener en el ServerSession, así:

serverSession.addListener(new ServerSession.RemoveListener() 
{ 
    public void removed(ServerSession session, boolean timeout); 
    { 
     if (!timeout) 
     { 
      // Explicitly disconnected, invalidate the HttpSession 
      httpCometDMapper.invalidate(session); 
     } 
    } 
}); 

Este oyente relojes para desconexiones explícitas desde el cliente y el servidor (- cuidado de reentrada).

Un poco más difícil es implementar el mismo mecanismo para desconexiones no explícitas.En este caso, el parámetro timeout será verdadero, pero podría haber ocurrido debido a una falla temporal de la red (en lugar de que el cliente desaparezca para siempre), y el mismo usuario ya puede haber vuelto a apretones de manos con un nuevo ServerSession.

Creo que en este caso un tiempo de espera de la aplicación podría resolver el problema: cuando vea un ServerSession eliminado debido a un tiempo de espera, se fija en ese usuario y se inicia un tiempo de espera de la aplicación. Si el mismo usuario vuelve a apretones de manos, cancele el tiempo de espera de la aplicación; de lo contrario, el usuario se habrá ido, el tiempo de espera de la aplicación expirará e invalidará también el HttpSession.

Lo anterior son solo ideas y sugerencias; la implementación real depende en gran medida de los detalles de la aplicación (y es por eso que CometD no la proporciona de fábrica).

Los puntos clave son el asignador, el HttpSessionListener y el RemoveListener, y conocer los ciclos de vida de esos componentes. Una vez que lo administre, puede escribir el código correcto que haga lo correcto para su aplicación.

Por último, cabe destacar que CometD tiene una manera independiente del transporte de interactuar con el HttpSession a través de la BayeuxContext ejemplo, que se puede obtener de BayeuxServer.getContext(). Sugiero que lo mires también para ver si puede simplificar las cosas, especialmente para recuperar tokens almacenados en el HttpSession.

0

¿Hay algún problema si vamos a crear un BayeuxClient después de la falla de conexión temporal?

Puede probar con este código a continuación.

try { 
     log.info("Running streaming client example...."); 
     makeConnect(); 


    } catch (Exception e) { 
     handleException("Error while setup the salesforce connection.", e); 
    } 
} 



private void makeConnect() { 
    try{ 
     client = makeClient(); 
     client.getChannel(Channel.META_HANDSHAKE).addListener 
       (new ClientSessionChannel.MessageListener() { 
        public void onMessage(ClientSessionChannel channel, Message message) { 
         log.info("[CHANNEL:META_HANDSHAKE]: " + message); 
         boolean success = message.isSuccessful(); 
         if (!success) { 
          String error = (String) message.get("error"); 
          if (error != null) { 
           log.error("Error during HANDSHAKE: " + error); 
          } 

          Exception exception = (Exception) message.get("exception"); 
          if (exception != null) { 
           handleException("Exception during HANDSHAKE: ", exception); 
          } 
         } 
        } 
       }); 

     client.getChannel(Channel.META_CONNECT).addListener(
       new ClientSessionChannel.MessageListener() { 
        public void onMessage(ClientSessionChannel channel, Message message) { 
         log.info("[CHANNEL:META_CONNECT]: " + message); 
         boolean success = message.isSuccessful(); 

         if (!success) { 
          client.disconnect(); 
          makeConnect(); 
          String error = (String) message.get("error"); 
          if (error != null) { 
           //log.error("Error during CONNECT: " + error); 
          } 
         } 
        } 

       }); 

     client.getChannel(Channel.META_SUBSCRIBE).addListener(
       new ClientSessionChannel.MessageListener() { 
        public void onMessage(ClientSessionChannel channel, Message message) { 
         log.info("[CHANNEL:META_SUBSCRIBE]: " + message); 
         boolean success = message.isSuccessful(); 
         if (!success) { 
          String error = (String) message.get("error"); 
          if (error != null) { 
           makeConnect(); 
           log.error("Error during SUBSCRIBE: " + error); 
          } 
         } 
        } 
       }); 
     client.handshake(); 
     log.info("Waiting for handshake"); 
     boolean handshaken = client.waitFor(waitTime, BayeuxClient.State.CONNECTED); 
     if (!handshaken) { 
      log.error("Failed to handshake: " + client); 
     } 
     log.info("Subscribing for channel: " + channel); 
     client.getChannel(channel).subscribe(new MessageListener() { 
      public void onMessage(ClientSessionChannel channel, Message message) { 
       injectSalesforceMessage(message); 
      } 
     }); 
     log.info("Waiting for streamed data from your organization ..."); 
    }catch (Exception e) { 
     handleException("Error while setup the salesforce connection.", e); 
    } 

} 
Cuestiones relacionadas