Cómo invalidar una sesión de usuario cuando inicia sesión dos veces con las mismas credenciales

Estoy usando JSF 1.2 con Richfaces y Facelets.

Tengo una aplicación con muchos beans de ámbito de sesión y algunos beans de aplicación.

El usuario inicia sesión con, digamos, Firefox. Se crea una sesión con ID = “A”; Luego abre Chrome y vuelve a iniciar sesión con las mismas credenciales. Se crea una sesión con ID = “B”.

Cuando se crea la sesión “B”, quiero poder destruir la sesión “A”. ¿Como hacer eso?

También. cuando el usuario de Firefox hace algo, quiero poder mostrar una ventana emergente o algún tipo de notificación que diga “Has cerrado la sesión porque has iniciado sesión desde otro lugar”.

Tengo un sessionListener que realiza un seguimiento de las sesiones creadas y destruidas. La cuestión es que podría guardar el objeto HTTPSession en un bean con ámbito de aplicación y destruirlo cuando detecte que el usuario ha iniciado sesión dos veces. Pero algo me dice que simplemente está mal y no funcionará.

¿JSF realiza un seguimiento de las sesiones en algún lugar del lado del servidor? ¿Cómo acceder a ellos por identificador? Si no, ¿cómo patear el primer inicio de sesión de un usuario cuando inicia sesión dos veces?

El enfoque independiente de la base de datos permitiría al User tener una variable static Map e implementar HttpSessionBindingListener (y Object#equals() y Object#hashCode() ). De esta forma, su aplicación seguirá funcionando después de un locking imprevisto que puede hacer que los valores DB no se actualicen (por supuesto, puede crear ServletContextListener que restablece el DB en el inicio de la aplicación web, pero eso es solo más trabajo).

Así es como debe verse el User :

 public class User implements HttpSessionBindingListener { // All logins. private static Map logins = new ConcurrentHashMap<>(); // Normal properties. private Long id; private String username; // Etc.. Of course with public getters+setters. @Override public boolean equals(Object other) { return (other instanceof User) && (id != null) ? id.equals(((User) other).id) : (other == this); } @Override public int hashCode() { return (id != null) ? (this.getClass().hashCode() + id.hashCode()) : super.hashCode(); } @Override public void valueBound(HttpSessionBindingEvent event) { HttpSession session = logins.remove(this); if (session != null) { session.invalidate(); } logins.put(this, event.getSession()); } @Override public void valueUnbound(HttpSessionBindingEvent event) { logins.remove(this); } } 

Cuando ingresa al User siguiente manera:

 User user = userDAO.find(username, password); if (user != null) { sessionMap.put("user", user); } else { // Show error. } 

luego invocará el valueBound() que eliminará cualquier usuario previamente conectado del mapa de valueBound() de logins e invalidará la sesión.

Cuando cierra la sesión del User siguiente manera:

 sessionMap.remove("user"); 

o cuando se agota el tiempo de espera de la sesión, se valueUnbound() el valueUnbound() que elimina al usuario del mapa de valueUnbound() de logins .

  1. crear un campo entero en el databse userLoggedInCount
  2. En cada incremento de inicio de sesión marque y almacene el resultado en la sesión.
  3. En cada solicitud, compruebe el valor en la base de datos y el de la sesión, y si el de la sesión es menor que el de la base de datos, invalidate() la sesión y disminuya el valor en la base de datos.
  4. cada vez que se destruye una sesión, disminuye el valor también.

Me gusta la respuesta de BalusC con HttpSessionBindingListener.

Pero en Enterprise JavaBeansTM Specification, Version 2.0 está escrito:

Un Enterprise Bean no debe usar campos estáticos de lectura / escritura. Se permite el uso de campos estáticos de solo lectura. Por lo tanto, se recomienda que todos los campos estáticos en la clase Enterprise Bean se declaren como finales.

Entonces, ¿no es mejor hacer un Bean ApplicationScoped que almacene la aplicación de tabla sin usar campos estáticos?

Lo probó y parece funcionar …

Aquí está mi ejemplo:

 @Named @ApplicationScoped public class UserSessionStorage implements java.io.Serializable,HttpSessionBindingListener { @Inject UserManagement userManagement; private static final long serialVersionUID = 1L; /** * Application wide storage of the logins */ private final Map> logins = new HashMap>(); @Override public void valueBound(final HttpSessionBindingEvent event) { System.out.println("valueBound"); /** * Get current user from userManagement... */ User currentUser = userManagement.getCurrentUser(); List sessions = logins.get(currentUser); if (sessions != null) { for (HttpSession httpSession : sessions) { httpSession.setAttribute("invalid", "viewExpired"); } } else { sessions = new ArrayList(); } HttpSession currentSession = event.getSession(); sessions.add(currentSession); logins.put(currentUser, sessions); } @Override public void valueUnbound(final HttpSessionBindingEvent event) { System.out.println("valueUnbound"); User currentUser = userManagement.getCurrentUser(); List sessions = logins.get(currentUser); if (sessions != null) { sessions.remove(event.getSession()); } else { sessions = new ArrayList(); } logins.put(currentUser, sessions); } 

}

-> Perdón por mi inglés …