How to invalidate an user session when he logs twice with the same credentials
facelets, java, jsf, richfaces, session
Solution
The DB-independent approach would be to let the `User` have a `static Map<User, HttpSession>` variable and implement `HttpSessionBindingListener` (and `Object#equals()` and `Object#hashCode()`). This way your webapp will still function after an unforeseen crash which may cause that the DB values don't get updated (you can of course create a `ServletContextListener` which resets the DB on webapp startup, but that's only more and more work).
Here's how the `User` should look like:
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);
}
}
When you login the `User` as follows:
User user = userDAO.find(username, password);
if (user != null) {
sessionMap.put("user", user);
} else {
// Show error.
}
then it will invoke the `valueBound()` which will remove any previously logged in user from the `logins` map and invalidate the session.
When you logout the `User` as follows:
sessionMap.remove("user");
or when the session is timed out, then the `valueUnbound()` will be invoked which removes the user from the `logins` map.
Problem
I'm using JSF 1.2 with Richfaces and Facelets. I have an application with many session-scoped beans and some application beans. The user logs in with, let's say, Firefox. A session is created with ID="A"; Then he opens Chrome and logs in again with the same credentials. A session is created with ID="B". When the session "B" is created, I want to be able to destroy session "A". How to do that? Also. when the user in Firefox does anything, I want to be able to display a popup or some kind of notification saying "You have been logged out because you have logged in from somewhere else". I have a sessionListener who keeps track of the sessions created and destroyed. The thing is, I could save the HTTPSession object in a application-scoped bean and destroy it when I detect that the user has logged in twice. But something tells me that is just wrong and won't work. Does JSF keep track of the sessions somewhere on the server side? How to access them by identifier? If not, how to kick out the first log in of an user when he logs in twice?