2010-03-03 8 views
19

Używam JSF 1.2 z funkcjami Richfaces i Facelets.Jak unieważnić sesję użytkownika podczas dwukrotnego logowania przy użyciu tych samych poświadczeń

Mam aplikację zawierającą wiele ziaren o zasięgu sesji i niektóre komponenty bean aplikacji.

Użytkownik loguje się za pomocą, powiedzmy, Firefoksa. Sesja jest tworzona z identyfikatorem = "A"; Następnie otwiera Chrome i loguje się ponownie z tymi samymi danymi uwierzytelniającymi. Sesja jest tworzona z identyfikatorem = "B".

Po utworzeniu sesji "B" chcę móc zniszczyć sesję "A". Jak to zrobić?

Również. gdy użytkownik w Firefoksie robi cokolwiek, chcę móc wyświetlić wyskakujące okienko lub powiadomienie "Wylogowano się, ponieważ zalogowałeś się z innego miejsca".

Mam sessionListener, który śledzi sesje utworzone i zniszczone. Chodzi o to, że mógłbym zapisać obiekt HTTPSession w zbiorze o zasięgu aplikacji i zniszczyć go, gdy wykryję, że użytkownik zalogował się dwa razy. Ale coś mi mówi, że jest po prostu źle i nie zadziała.

Czy JSF śledzi sesje gdzieś po stronie serwera? Jak uzyskać do nich dostęp za pomocą identyfikatora? Jeśli nie, to w jaki sposób wykluczyć pierwsze logowanie użytkownika po dwukrotnym zalogowaniu?

Odpowiedz

19

DB niezależne rozwiązaniem byłoby, aby umożliwić User mają zmienną static Map<User, HttpSession> i wdrożenie HttpSessionBindingListener (i Object#equals() i Object#hashCode()). W ten sposób twoja aplikacja będzie nadal działała po nieprzewidzianej awarii, która może spowodować, że wartości DB nie zostaną zaktualizowane (możesz oczywiście utworzyć ServletContextListener, która resetuje DB przy uruchomieniu aplikacji Webapp, ale to tylko więcej pracy).

Oto jak User powinna wyglądać następująco:

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

} 

podczas logowania User następująco:

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

następnie będzie ona wywołać valueBound() który usunie wszelkie poprzednio zalogowanego użytkownika z logins mapować i unieważnić sesję.

Po wylogowaniu się User następująco:

sessionMap.remove("user"); 

lub gdy sesja jest limit czasu, wtedy valueUnbound() zostanie wywołany który usuwa użytkownika z mapą logins.

+0

Dzięki za odpowiedź. Przypuszczam, że "sessionMap.put (" użytkownik ", użytkownik);" powinno być "sessionMap.put (nazwa użytkownika, użytkownik);". W przeciwnym razie, jeśli zaloguje się inny użytkownik z różnymi referencjami, wykopilibyśmy pierwszego użytkownika. – pakore

+0

To nie jest normalne. Nie chcesz mieć różnych zalogowanych użytkowników podczas jednej sesji klienta. Nie należy również mylić mapy sesji z mapą aplikacji. – BalusC

+0

Ok Teraz rozumiem, że sessionMap to ExternalContext.sessionMap. Działa :). – pakore

4
  1. utworzyć pole całkowitą w databse userLoggedInCount
  2. Na każde kolejne logowania, że ​​flaga i zapisać wynik w sesji.
  3. Na każde żądanie sprawdzić wartość w bazie danych i jeden w sesji, a jeżeli jeden w sesji jest mniejsza niż w DB, invalidate() sesji i zmniejszyć wartość w bazie
  4. ilekroć sesja ulega zniszczeniu również zmniejsza wartość.
+0

zamiast DB i można użyć zgadywania klasy aplikacji, jak sądzę. Chodzi o to, jak uzyskać na przykład dostęp do odbiornika fazowego? Dobre rozwiązanie. – pakore

+0

cóż, można uzyskać ExternalContext przez FaceContext – Bozho

+1

Ten algorytm może nie być wystarczający. Teoria jest idealna, ale w praktyce nie będzie działać, jeśli przeglądarka zapisuje sesję w pliku cookie i przywraca ją z powrotem. Zajrzyj do kroku 2. Jeśli, jak powiedziałem, przeglądarka automatycznie przywróci sesję, nie przejdzie ona przez żaden LoginHandler lub LoginMethod ani żadne sterowane miejsce, aby wykonać przyrost tej flagi. Jak działać w tym przypadku? – ElPiter

1

Podoba mi się odpowiedź od BalusC z HttpSessionBindingListener.

Ale w Enterprise JavaBeansTM Specification, wersja 2.0 nie jest napisane:

Bean przedsiębiorstwo nie musi korzystać z odczytu/zapisu pól statycznych. Używanie tylko statycznych pól tylko do odczytu jest dozwolone . Dlatego zaleca się, aby wszystkie statycznych pól w klasie fasoli przedsiębiorstw być zadeklarowane jako ostateczny

Więc isnt't lepiej zrobić ApplicationScoped Bean które przechowują wniosek stół szeroki bez użycia pól statycznych ???

ona próbowała go i wydaje się działać ...

Oto mój przykład:

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

}

-> Przepraszam za mój änglish ...

+0

'HttpSessionBindingListener' nie jest EJB. Tylko te klasy z główną adnotacją 'javax.ejb. *', Taką jak '@ Stateless', to EJB. – BalusC

Powiązane problemy