How to invalidate an user session when he logs twice with the same credentials

后端 未结 3 1835
粉色の甜心
粉色の甜心 2020-11-27 17:00

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\'

相关标签:
3条回答
  • 2020-11-27 17:38

    I like the answer from BalusC with a HttpSessionBindingListener.

    But in Enterprise JavaBeansTM Specification, Version 2.0 there is written:

    An enterprise Bean must not use read/write static fields. Using read-only static fields is allowed. Therefore, it is recommended that all static fields in the enterprise bean class be declared as final

    So isnt't it better to make an ApplicationScoped Bean which store the table application wide without using static fields???

    It tried it out and it seems to work...

    Here is my example:

    @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);
    }
    

    }

    -> Sorry for my änglish...

    0 讨论(0)
  • 2020-11-27 17:52

    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.

    0 讨论(0)
  • 2020-11-27 17:56
    1. create an integer field in the databse userLoggedInCount
    2. On each login increment that flag and store the result in the session.
    3. On each request check the value in the database and the one in the session, and if the one in the session is less than the one in the DB, invalidate() the session and decrement the value in the database
    4. whenever a session is destroyed decrement the value as well.
    0 讨论(0)
提交回复
热议问题