Как аннулировать сеанс пользователя, когда он дважды регистрируется с одинаковыми учетными данными

Я использую JSF 1.2 с Richfaces и Facelets.

У меня есть приложение со многими сеансовыми beans и некоторым приложением beans.

Пользователь входит в систему, скажем, через Firefox. Сессия создается с ID = "A"; Затем он открывает Chrome и регистрируется снова с теми же учетными данными. Сессия создается с ID = "B".

Когда создается сеанс "B", я хочу иметь возможность уничтожить сеанс "A". Как это сделать?

Кроме того. когда пользователь в Firefox что-то делает, я хочу иметь возможность отображать всплывающее окно или какое-то уведомление, в котором говорится: "Вы вышли из системы, потому что вы вошли в систему из другого места".

У меня есть sessionListener, который отслеживает созданные и уничтоженные сеансы. Дело в том, что я мог бы сохранить объект HTTPSession в области bean с приложениями и уничтожить его, когда обнаружил, что пользователь дважды зашел в систему. Но что-то говорит мне, что это просто неправильно и не будет работать.

Поддерживает ли JSF сеансы где-то на стороне сервера? Как получить к ним доступ по идентификатору? Если нет, как запустить первый вход пользователя при входе в систему дважды?

Ответ 1

Независимый от DB подход должен был бы позволить User иметь переменную static Map<User, HttpSession> и реализовать HttpSessionBindingListenerObject#equals() и Object#hashCode()). Таким образом, ваш webapp по-прежнему будет функционировать после непредвиденного сбоя, что может привести к тому, что значения DB не будут обновляться (вы можете, конечно, создать ServletContextListener, который сбрасывает БД при запуске webapp, но это все больше и больше работает).

Вот как выглядит 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);
    }

}

Когда вы входите в систему User следующим образом:

User user = userDAO.find(username, password);
if (user != null) {
    sessionMap.put("user", user);
} else {
    // Show error.
}

то он вызовет valueBound(), который удалит любого ранее зарегистрированного пользователя с карты logins и аннулирует сеанс.

Когда вы выходите из системы User следующим образом:

sessionMap.remove("user");

или при завершении сеанса будет вызываться valueUnbound(), который удаляет пользователя из карты logins.

Ответ 2

  • создать целочисленное поле в базе данных userLoggedInCount
  • При каждом приращении имени этого флага и сохранении результата в сеансе.
  • В каждом запросе проверьте значение в базе данных и то, что в сеансе, и если одно в сеансе меньше, чем одно в БД, invalidate() сеанс и уменьшите значение в базе данных
  • всякий раз, когда сеанс уничтожается, декремент также уменьшается.

Ответ 3

Мне нравится ответ от BalusC с HttpSessionBindingListener.

Но в спецификации Enterprise JavaBeansTM версии 2.0 написано:

Предприятие Bean не должно использовать статические поля чтения/записи. Использование статических полей только для чтения позволил. Поэтому рекомендуется, чтобы все статические поля в корпоративном классе Beanобъявлено окончательным

Итак, не лучше ли сделать ApplicationScoped Bean, который хранит приложение таблицы широко, не используя статические поля???

Он пробовал и, похоже, работает...

Вот мой пример:

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

}

- > Извините за мой английский...