2010-03-03 3 views
19

Я использую JSF 1.2 с Richfaces и Facelets.Как аннулировать сеанс пользователя, когда он дважды регистрируется с теми же учетными данными

У меня есть приложение со многими фасолью с сессией и некоторыми компонентами приложения.

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

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

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

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

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

ответ

19

БД-независимый подход был бы позволить 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 карты.

+0

Спасибо за ответ. Я предполагаю, что «sessionMap.put (« пользователь », пользователь);» должен быть «sessionMap.put (имя пользователя, пользователь);». В противном случае, если другой пользователь с разными учетными данными войдет в систему, мы вышлем первого пользователя. – pakore

+0

Это не нормально. Вы не хотите, чтобы во время одного клиентского сеанса были разные зарегистрированные пользователи. Также не путайте карту сеанса с картой приложения. – BalusC

+0

Хорошо, теперь я понимаю, что sessionMap является ExternalContext.sessionMap. Он работает :). – pakore

4
  1. создать целое поле в Databse userLoggedInCount
  2. На каждом входе в приращении, что флаг и сохранить результат в сессии.
  3. На каждом запросе проверить значение в базе данных и один в сессии, и если один в сессии меньше, чем в БД, invalidate() сессии и уменьшения значения в базе данных
  4. всякий раз, когда сеанс уничтожен и уменьшает значение.
+0

вместо Я могу использовать класс, охваченный областью применения. Дело в том, как получить доступ к нему от фазового слушателя, например? Хорошее решение. – pakore

+0

ну, вы можете получить ExternalContext через FacesContext – Bozho

+1

Этот алгоритм может оказаться недостаточным. Теория идеальна, но на практике она не будет работать, если браузер сохранит сессию в файле cookie и восстановит ее обратно. Взгляните на Шаг 2. Если, как я уже сказал, просмотр автоматически возобновит сеанс, он не будет проходить через любой LoginHandler или LoginMethod или любое контролируемое место для выполнения приращения этого флага. Как действовать в этом случае? – ElPiter

1

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

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

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

Так isnt't лучше сделать 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); 
} 

}

-> Извините за мой änglish ...

+0

«HttpSessionBindingListener» не является EJB. Только те классы с главной аннотацией 'javax.ejb. *', Такие как '@ Stateless', являются EJB. – BalusC

Смежные вопросы