java - autenticacion - Cómo invalidar una sesión de usuario cuando inicia sesión dos veces con las mismas credenciales
saml autenticacion (3)
- crear un campo entero en la base de datos
userLoggedInCount
- En cada inicio de sesión, incremente ese indicador y almacene el resultado en la sesión.
- En cada solicitud, verifique 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 de la base de datos. - cada vez que se destruye una sesión, decrementa el valor también.
Estoy usando JSF 1.2 con Richfaces y Facelets.
Tengo una aplicación con muchos beans 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 en Firefox hace algo, quiero poder mostrar una ventana emergente o algún tipo de notificación que diga "Se ha desconectado porque ha 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 de ámbito de aplicación y destruirlo cuando detecto que el usuario ha iniciado sesión dos veces. Pero algo me dice que 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 es así, ¿cómo eliminar el primer inicio de sesión de un usuario cuando inicia sesión dos veces?
Me gusta la respuesta de BalusC con un HttpSessionBindingListener.
Pero en Enterprise JavaBeansTM Specification, la versión 2.0 está escrita:
Un Bean empresarial 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 de enterprise bean se declaren como finales
Entonces, ¿no es mejor hacer un ApplicationScoped Bean que almacene toda la aplicación de la 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<User, List<HttpSession>> logins = new HashMap<User, List<HttpSession>>();
@Override
public void valueBound(final HttpSessionBindingEvent event) {
System.out.println("valueBound");
/**
* Get current user from userManagement...
*/
User currentUser = userManagement.getCurrentUser();
List<HttpSession> sessions = logins.get(currentUser);
if (sessions != null) {
for (HttpSession httpSession : sessions) {
httpSession.setAttribute("invalid", "viewExpired");
}
} else {
sessions = new ArrayList<HttpSession>();
}
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<HttpSession> sessions = logins.get(currentUser);
if (sessions != null) {
sessions.remove(event.getSession());
} else {
sessions = new ArrayList<HttpSession>();
}
logins.put(currentUser, sessions);
}
}
-> Lo siento por mi änglish ...
El enfoque independiente de la base de datos sería permitir que el User
tenga una variable de static Map<User, HttpSession>
e implementar HttpSessionBindingListener
(y Object#equals()
y Object#hashCode()
). De esta manera, su aplicación web seguirá funcionando después de un bloqueo imprevisto que puede hacer que los valores de la base de datos no se actualicen (por supuesto, puede crear un ServletContextListener
que restablece la base de datos en el inicio de la aplicación web, pero eso es solo un trabajo cada vez más).
Así es como debe verse el User
:
public class User implements HttpSessionBindingListener {
// All logins.
private static Map<User, HttpSession> 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 inicia sesión el 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 que haya iniciado sesión previamente del mapa de valueBound()
de logins
e invalidará la sesión.
Cuando cierre la sesión del User
siguiente manera:
sessionMap.remove("user");
o cuando se agote el tiempo de espera de la sesión, se valueUnbound()
que elimina al usuario de la valueUnbound()
de logins
.