Как сделать недействительным сеанс пользователя, когда он дважды регистрируется с одинаковыми учетными данными - PullRequest
19 голосов
/ 03 марта 2010

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

У меня есть приложение с множеством bean-компонентов в сессионной области и некоторых bean-компонентов приложения.

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

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

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

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

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

Ответы [ 3 ]

20 голосов
/ 03 марта 2010

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

Вот как должен выглядеть 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.

4 голосов
/ 03 марта 2010
  1. создать целое поле в базе данных userLoggedInCount
  2. При каждом увеличении входа в систему этот флаг и сохранять результат в сеансе.
  3. При каждом запросе проверять значение вВ базе данных и в сеансе, а если в сеансе меньше, чем в БД, invalidate() сеанс и уменьшать значение в базе данных
  4. всякий раз, когда сеанс уничтожается, уменьшать значениеа также.
1 голос
/ 17 июня 2013

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

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

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

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

Это опробовано и, кажется, работает ...

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

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

}

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

...