Disable multiple logins for same user - Servlets and Sessions

in #programming6 years ago

There are many approaches including the database flag for a user to know whether a user is logged in currently or not. Database solution takes a lot of work and amount of time to implement and has many problems including browser close should also log-out for the user does not wait so many minutes before sessions get invalidated automatically. HttpSessionListener must also be implemented in the database solution, hence I will strongly discourage the use of this approach, which involves re-inventing the wheel again and again because every developer has to do it on his own for his application.

HttpSessionBindingListener
There are two ways users can be restricted to log-in from multiple sessions, either previous session of the user be removed, or new session of the user be disallowed. In both approaches, I would recommend using HttpSessionBindingListener. Implementing this interface requires providing implementation for
boolean equals(Object other),
int hashCode(),
void valueBound(HttpSessionBindingEvent event) and
void valueUnbound(HttpSessionBindingEvent event).

public class LoginDto implements HttpSessionBindingListener {
    private String accountId;
    private boolean alreadyLoggedIn;

    private static Map<User, HttpSession> logins = new HashMap<User, HttpSession>();

  //setters and getters of all your User dto including accountId...

  @Override
  public boolean equals(Object other) {
    return (other instanceof LoginDto) && (getAccountId() != null) ? 
     getAccountId().equals(((LoginDto) other).getAccountId()) : (other == this);
  }

  @Override
  public int hashCode() {
    return (getAccountId() != null) ? 
     (this.getClass().hashCode() + getAccountId().hashCode()) : super.hashCode();
  }

  @Override
  public void valueBound(HttpSessionBindingEvent event){
    HttpSession oldSession = logins.get(this);
    if (oldSession != null) {
      alreadyLoggedIn = true;
    } else {
      logins.put(this, event.getSession());
    }

    //Note: you can comment above code and remove comments from below code. removing comments from 
 //below code will remove old session of user and let the user log-in from new session.

    //HttpSession session = logins.remove(this);
    //if (session != null) {
    //  session.invalidate();
   //}
   //logins.put(this, event.getSession());  
  }

  @Override
  public void valueUnbound(HttpSessionBindingEvent event) {
    logins.remove(this);
  }

}//end of class LoginDto

You do not need to do anything else except storing a LoginDto in session when a user logs in or removing it on session timeout. Http Session Binding Listener should automatically invoke valueBound and valueUnbound methods whenever LoginDto is bound to the session, not to mention, browser's close or in cases when users leave unexpectedly.

Example servlet code

if(//user is valid and validated from database etc...) {
    session.setAttribute("UserContext", dto); //this will invoke valueBound method of LoginDto.
    if(dto.isAlreadyLoggedIn()){
      session.removeAttribute("UserContext"); //this will invoke valueUnbound method of LoginDto
      throw new MyCustomException("You are already logged in from a different session. Please logout first.");
    }
}else {
    session.setAttribute("UserContext", null);
}

When the user logs in, and you store the user in the session i.e. request.getSession.setAttribute("user", user); it will invoke the valueBound() method which is supposed to remove any previously logged in users from the logins map and invalidate the session. Similarly, when the user logs out and you remove it from session i.e. request.getSession().removeAttribute("user"); then the method valueUnbound() will be invoked which is supposed to remove the user from the logins map. valueUnbound() is also called when a session expires or times-out.

Coin Marketplace

STEEM 0.18
TRX 0.15
JST 0.028
BTC 63064.93
ETH 2468.39
USDT 1.00
SBD 2.55