Как аннулировать сеанс пользователя, когда он дважды регистрируется с одинаковыми учетными данными
Я использую 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>
и реализовать HttpSessionBindingListener
(и Object#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);
}
}
- > Извините за мой английский...