Java EE 6 Параллельный сеанс и совместное использование

Я работаю над веб-приложением с Java EE 6 (JSF CDI EJB), которое не должно разрешать одновременный вход в систему (один и тот же пользователь и пароль).

Что мне нравится:

Если пользователь дважды входит в систему, первый сеанс должен быть признан недействительным, а данные старого сеанса (включая все компоненты CDI с SessionScope или другими областями, такими как WindowScope из Apache CODI) переносятся в новый сеанс.

Это что-то вроде подхода к захвату разыскиваемой сессии :-)


person urbiwanus    schedule 16.07.2012    source источник
comment
Почему бы вам просто не скрыть страницу входа для уже вошедших в систему пользователей и отклонить любую попытку входа с сообщением типа Вы уже вошли в систему. Пожалуйста, выйдите из системы, если вы хотите войти в систему как другой пользователь. или что-то разумное в этом роде.   -  person BalusC    schedule 17.07.2012
comment
Это не так просто. потому что пользователь хочет работать на двух разных станциях, не заботясь о входе в систему. Он просто хочет видеть тот же результат и то же состояние приложения.   -  person urbiwanus    schedule 17.07.2012
comment
Тогда зачем вам аннулировать первую сессию?   -  person BalusC    schedule 17.07.2012
comment
Вторая сессия может изменить данные первой. Представьте, что вы открыли заказ в первом сеансе, затем вы изменили свое рабочее пространство и хотите снова отредактировать заказ во втором сеансе, но забыли выйти из системы (на этом этапе никаких изменений для сохранения каких-либо данных). Я не хочу аннулировать сеанс, просто хочу перенести все данные во вновь созданный   -  person urbiwanus    schedule 17.07.2012
comment
Компоненты MyFaces CODI сохраняются для окна и всех окон в сеансе. Если вы реплицируете содержимое сеанса, вы реплицируете и эти bean-компоненты (для всех окон).   -  person Dar Whi    schedule 19.07.2012


Ответы (3)


Вы можете использовать bean-компонент без сохранения состояния для пользователя, поэтому каждый раз, когда нужно пытаться войти в систему/повторно войти в систему, текущий сеанс становится недействительным (в начале процедуры входа в систему)

рассмотреть такой подход:

try {           
   session = request.getSession();   //the request is passed by another page or action
   if(session.getAttribute("user") != null) {

           //your code to forward or handle the existing user (re-log in/ do nothing etc.)
}
person Lucian Enache    schedule 17.07.2012

Я решил эту проблему с помощью фильтра

открытый класс SessionReplicationFilter реализует фильтр {

@Inject
SessionReplicationManager manager;

public SessionReplicationFilter() {
}


@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
    //Process chain first 
    if (chain != null) {
        chain.doFilter(request, response);
    }

    //check http request
    if (request instanceof HttpServletRequest) {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        // Retrieve the session and the principal (authenticated user)
        // The principal name is actually the username
        HttpSession session = httpRequest.getSession();
        Principal principal = httpRequest.getUserPrincipal();
        if (principal != null && principal.getName() != null && session != null) {
            manager.checkExistingSession(principal.getName(), session)) 
        }
    }

}

@Override
public void init(FilterConfig filterConfig) throws ServletException {

}

@Override
public void destroy() {

}

}

Менеджер смотрит вслед

Открытый класс @ApplicationScoped SessionReplicationManager {

private Map<String, HttpSession> map = new ConcurrentHashMap<String, HttpSession>();


public boolean checkExistingSession(String user, HttpSession session) {
    if (map.keySet().contains(user)) {
        if (!session.getId().equals(map.get(user).getId())) {
            System.out.println("User already logged in ");
            HttpSession oldSession = map.get(user);
            // copies all attributes from the old session to the new session (replicate the session)
            Enumeration<String> enumeration = oldSession.getAttributeNames();
            while (enumeration.hasMoreElements()) {
                String name = enumeration.nextElement();
                System.out.println("Chaning attribut " + name);
                session.setAttribute(name, oldSession.getAttribute(name));
            }
            // invalidates the old user session (this keeps one session per user)
            oldSession.invalidate();
            map.put(user, session);
            return true;
        }
    } else {
        System.out.println("Putting "+user+" into session cache");
        map.put(user, session);
        return false;
    }
    return false;
}

}

Он очень хорошо работает с аннотированными компонентами CoDI ViewScoped.

Если первый пользователь становится недействительным, каждый запрос (AJAX) вызывает исключение с истекшим сроком действия сеанса, которое можно легко обработать даже с помощью кнопки восстановления сеанса.

Единственная незначительная проблема с bean-компонентами с областью просмотра заключается в том, что они получают новый идентификатор представления. Поменяв их на исходные, все работает нормально.

Вещи, которые мне нужно добавить:

  • Автоматический выход (опрос ajax, веб-сокеты,...)
  • Какой-то реестр, где хранятся все viewscoped-id

В этом комментарии не хватает:

  • конфигурация web.xml

С уважением

person urbiwanus    schedule 17.07.2012
comment
Я надеюсь, что любой, кто посмотрит на ваш ответ, поймет, что это просто безумная утечка памяти (карта HttpSessions никогда не очищается). - person Radoslav H.; 11.03.2015
comment
Ты прав. HttpSessionListener , который отслеживает событие sessionDestroyed, здесь отсутствует. Слушатель удаляет все недействительные сеансы с карты (например, тайм-аут, выход из системы,...) - person urbiwanus; 11.03.2015

В Java EE 6 нет встроенного механизма для этого.

Поскольку я не могу себе представить, что вы хотите перенести что-то вроде текущего варианта использования (например, открытый процесс оформления заказа) из одного сеанса в другой, я предлагаю вам просто отследить состояние графического интерфейса пользователя.

URL-адреса RESTful звучат как идеальный подход для этого. Сохраните последний пользовательский URL/действие пользователя (например, www.myapp.com/orders/new/12) и повторно откройте его при новом входе в систему.

Если вы не хотите сохранять это в БД, идентификатор пользователя / URL-адрес карты в области приложения может быть способом KISS.

person jan groth    schedule 17.07.2012
comment
Я не уверен, что это будет работать во всех моих случаях использования. Но я попробую. - person urbiwanus; 17.07.2012
comment
Просто из любопытства - что это за система, которую вы строите? Почему пользователи заходят на разные станции в течение относительно короткого периода времени? - person jan groth; 17.07.2012
comment
Подумайте о чем-то вроде POS-системы с разными терминалами. Я не могу вдаваться в подробности. Мне жаль - person urbiwanus; 17.07.2012