Evite el inicio de sesión múltiple con el mismo nombre de usuario y contraseña

Estoy desarrollando una aplicación que necesita evitar el inicio de sesión múltiple con el mismo nombre de usuario y contraseña.

Si sucede en la misma máquina, entonces obviamente tenemos que hacer algo con la sesión del usuario, pero también debería evitar que se inicien sesión en máquinas diferentes con el mismo nombre de usuario y contraseña.

Tenemos que seguir teniendo en cuenta lo siguiente:

  1. Si el usuario cierra el navegador sin cerrar la sesión.
  2. Si el tiempo de espera de la sesión.

Le agradecería cualquier ayuda en esto.

Si el usuario cierra el navegador sin cerrar la sesión.

Particularmente este caso es difícil y no confiable de detectar. Puede usar el evento beforeunload en Javascript, pero depende totalmente de si el navegador tiene habilitado JS y si el navegador en particular admite este evento no estándar (por ejemplo, Opera no). Esa es también una de las principales razones por las que sugiero que simplemente cierre la sesión del usuario que inició sesión anteriormente en lugar de evitar el inicio de sesión. Eso también es más fácil de usar y seguro para el caso de que el usuario “olvide” cerrar la sesión desde la otra computadora.

La manera más fácil es dejar que el User tenga una variable de static Map y permita que implemente HttpSessionBindingListener (y Object#equals() y Object#hashCode() ).

 public class User implements HttpSessionBindingListener { // All logins. private static Map logins = new HashMap(); // 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) { request.getSession.setAttribute("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:

 request.getSession().removeAttribute("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 .

Cree una tabla en su base de datos, llamémosla [online_users] , con tres campos:

 [online_users] 1. username 2. login_time 3. logout_time 

Cada vez que un usuario inicia sesión, inserte el nombre del usuario y el tiempo de inicio de sesión en [online_users] en [online_users] .

En todas las páginas que requieren que los usuarios inicien sesión, [online_users] esta condición: verifique [online_users] para ver si el tiempo de logout_time de logout_time del usuario está en blanco o no.

Cada vez que un usuario presiona un botón de cerrar sesión, configure el tiempo de logout_time de logout_time en [online_users] en [online_users] para el nombre de ese usuario.

Si alguien intenta iniciar sesión con un nombre de usuario y contraseña activos, verifique el username de username y logout_time y muestre un mensaje que indique que el usuario ya logout_time sesión. Y, lo más importante, configure logout_time en MULTIPLELOGIN para ese usuario.

Si ese usuario está conectado en cualquier otra máquina, entonces si actualiza o navega a otra página, el sitio le dirá que se ha desconectado. Luego, el usuario puede ser redirigido a la página de inicio del sitio.

Tome un campo adicional en la tabla con el nombre de la columna, diga “IsLoggedIn” como campo de bit y configúrelo como verdadero hasta que el usuario inicie sesión. Tan pronto como el usuario cierre la sesión, configúrelo en falso. Esto debe hacerse también para el tiempo de caducidad de la sesión. Tan pronto como la sesión expire, este campo debe establecerse en falso automáticamente mediante desencadenadores o mediante una llamada a SP.

una buena solución es bienvenida

También recomendaría la solución de Shantanu Gupta: tener una columna de base de datos que indique que el usuario está actualmente conectado y actualizar esa columna en consecuencia.

Para ‘capturar’ la caducidad de la sesión, debe definir en su web.xml :

  com.foo.MySessionListener  

Donde MySessionListener es su implementación de la interfaz HttpSessionListener (proporcionada por la API de Servlet).

Simplemente sugeriría usar un marco de seguridad para manejar todos estos detalles por usted. Spring Security , por ejemplo, es bastante fácil de integrar en un proyecto existente, se puede personalizar bastante si es necesario y, lo que es más importante, tiene soporte integrado para detectar y controlar inicios de sesión concurrentes .

No reinvente la rueda cuando no la necesite, de lo contrario terminará pasando un buen tiempo para crear una rueda con baches.

Tal vez demasiado simplificado, pero bueno … me funciona en Web2Py:

Solo en el inicio de sesión exitoso, estoy escribiendo el SessionID (response.session_id) en la tabla auth_membership. En la página de destino (página de índice) compruebo si el response.session_id actual es igual al SessionID que proviene de la base de datos. Si es así, todo está bien. Si no, (el usuario “mayor”, primero) se desconecta amablemente.

Lo anterior funciona ya que con cada inicio de sesión se crea y almacena una nueva response.session_id en la base de datos. La verificación se realiza solo en la página de destino (que en mi aplicación es la más importante, inicia muchas otras funciones), por lo que no hay demasiadas visitas a la base de datos para lo anterior. Lo anterior no depende de que el usuario cierre la sesión. No se trata de una dirección IP (que otros han mencionado, sufre de sus propios problemas). Solo permite que un usuario inicie sesión a la vez y cierra la sesión del usuario “anterior”.

Espero que ayude a NeoToren

Puede almacenar algún tipo de ID de sesión para el usuario al iniciar sesión. Cuando el usuario cierra sesión o cuando la sesión expira, usted elimina esa información nuevamente.

Cuando un usuario intenta iniciar sesión y usted ya tiene un ID de sesión almacenado para este usuario, permita que el usuario lo confirme y luego invalide la sesión anterior.

Un usuario sin duda querrá volver a iniciar sesión inmediatamente si el navegador se bloqueó o algo así, por lo que dejar que el usuario espere a que expire la sesión puede ser molesto.

¿Tiene esto sentido para su aplicación?

Haría un seguimiento de la última dirección IP conocida de cada usuario y una marca de tiempo para cuando estuvieron por última vez en esa IP. Luego, puede bloquear el acceso de otras direcciones IP durante 5 minutos, una hora o lo que quiera.

Cada vez que cambia la dirección IP, puede a) caducar la sesión anterior del usuario, por lo que se ven obligados a volver a iniciar sesión yb) boost un contador por usuario (que puede poner a cero cada hora). Si el contador supera los 5 (o algo así), puede bloquear todo el acceso a la cuenta del usuario durante un período de tiempo más largo.

Esto se puede implementar fácilmente si tienes sesión. Para cada inicio de sesión del navegador, debe crear un registro de sesión en la sesión DB. El ID de sesión se puede usar como cookie de autenticación. La sesión DB también tiene un índice con nombre de usuario. Al iniciar sesión, puede consultar la base de datos para verificar cuántas sesiones hay. De hecho, permitimos una sesión para cada tipo. Por ejemplo, el usuario puede tener un inicio de sesión desde un teléfono móvil y otro desde el navegador. Pero no puede tener 2 sesiones de navegador.

Para resolver el problema que mencionaste. Tienes 2 opciones,

  1. Tener un tiempo de espera de sesión muy corto (como 5 minutos) y extender la sesión en cada uso. De esta forma, el usuario se cerrará automáticamente si se va sin cerrar la sesión.

  2. Tope la otra sesión. La nueva sesión choca con la sesión anterior. La sesión golpeada permanece en DB con una bandera especial durante 24 horas. Mostramos un mensaje para decirle al usuario que la otra sesión está siendo golpeada y muestra la hora y el IP. De esta manera, el usuario recibirá una notificación si su cuenta se ve comprometida.

Implementé una posible solución para mí,

en el loginFilter que uso, establezco un lastloggedin, userloggedin y userSession dentro del registro del usuario en mi sistema.

  user.setUser_lastlogged(new Date()); user.setUser_loggedin(true); user.setSessionId(request.getSession().getId()); appService.saveUsers(user); 

así que cuando voy a cualquiera de mis acciones de struts2 tengo un fragmento de código en el método de preparación.

 @Override public void prepare() throws Exception { UsersBase usercheck = appservice.getUserByUsername((String)request.getSession().getAttribute("j_username")); if(request.getSession().getId().equals(usercheck.getSessionId())){ request.getSession().invalidate(); } } 

Esto registrará al usuario cuando inicie sesión en otra máquina, o si no desea iniciar sesión, podría hacer lo siguiente en el archivo de inicio de sesión.

  UsersBase userdto = appService.getUserByUsername(username); if (userdto != null) { if ((userdto.getUser_loggedin())) { if (request.getSession().getId().equals(userdto.getSessionId())) { authRequest.eraseCredentials(); request.getSession().setAttribute("error", "You are already logged in "); } } } 

Usa el token

Cuando el usuario inicia sesión con éxito, el lado del servidor devuelve una cadena de token al lado del cliente / navegador, y el lado del servidor guarda un mapa con el token de identificación del usuario. El cliente revisa / solicita repetidamente al servidor con ese token, si el token no es el mismo, este usuario registra varias veces.

Al cerrar la sesión, guarda el token en cookies o sistema de archivos en el lado del cliente, y trae este token al iniciar sesión la próxima vez.

Mesa:

 userid:token:log_date