Frage Wie man eine Benutzersitzung ungültig macht, wenn er sich zweimal mit den gleichen Anmeldeinformationen anmeldet


Ich benutze JSF 1.2 mit Richfaces und Facelets.

Ich habe eine Anwendung mit vielen sitzungsspezifischen Beans und einigen Anwendungs-Beans.

Der Benutzer meldet sich an, sagen wir Firefox. Eine Sitzung wird mit ID = "A" erstellt; Dann öffnet er Chrome und meldet sich erneut mit den gleichen Anmeldeinformationen an. Eine Sitzung wird mit ID = "B" erstellt.

Wenn die Sitzung "B" erstellt wird, möchte ich die Sitzung "A" zerstören können. Wie geht das?

Ebenfalls. Wenn der Benutzer in Firefox etwas tut, möchte ich in der Lage sein, ein Popup oder eine Art Benachrichtigung anzuzeigen, die sagt "Du wurdest ausgeloggt, weil du dich von woanders eingeloggt hast".

Ich habe einen SessionListener, der die erstellten und zerstörten Sessions verfolgt. Die Sache ist, ich könnte das HTTPSession-Objekt in einer Application-Scoped-Bean speichern und es zerstören, wenn ich feststelle, dass der Benutzer sich zweimal angemeldet hat. Aber etwas sagt mir, das ist einfach falsch und wird nicht funktionieren.

Verfolgt JSF die Sitzungen irgendwo auf der Serverseite? Wie man auf sie durch Bezeichner zugreift? Wenn nicht, wie kann man das erste Einloggen eines Benutzers bei zweimaliger Anmeldung abbrechen?


19
2018-03-03 15:04


Ursprung


Antworten:


Der DB-unabhängige Ansatz wäre, das zu lassen User habe einen static Map<User, HttpSession> variabel und implementieren HttpSessionBindingListener (und Object#equals() und Object#hashCode()). Auf diese Weise funktioniert Ihre Webanwendung auch nach einem unvorhergesehenen Absturz, der dazu führen kann, dass die DB - Werte nicht aktualisiert werden (Sie können natürlich eine ServletContextListener was die DB beim Start von webapp zurücksetzt, aber das funktioniert nur mehr und mehr).

Hier ist wie das User Sollte aussehen, wie:

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

}

Wenn Sie sich einloggen, User wie folgt:

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

dann wird es das beschwören valueBound() das entfernt jeden vorher eingeloggten Benutzer von der logins Ordnen Sie die Sitzung zu und machen Sie sie ungültig.

Wenn Sie sich abmelden User wie folgt:

sessionMap.remove("user");

oder wenn die Sitzung abgelaufen ist, dann valueUnbound() wird aufgerufen, die den Benutzer aus dem entfernt logins Karte.


19
2018-03-03 16:25



  1. Erstellen Sie ein Integer-Feld in der Datenbank userLoggedInCount
  2. Bei jedem Login-Inkrement wird das Ergebnis in der Sitzung markiert und gespeichert.
  3. Überprüfen Sie bei jeder Anfrage den Wert in der Datenbank und den in der Sitzung, und wenn der Wert in der Sitzung kleiner als der in der Datenbank ist, invalidate() die Sitzung und dekrementieren den Wert in der Datenbank
  4. Wenn eine Sitzung zerstört wird, dekrementieren Sie den Wert ebenfalls.

4
2018-03-03 15:22



Ich mag die Antwort von BalusC mit einem HttpSessionBindingListener.

In Enterprise JavaBeansTM Specification, Version 2.0, steht geschrieben:

Ein Enterprise-Bean darf keine statischen Lese- / Schreibfelder verwenden. Verwenden schreibgeschützter statischer Felder ist   dürfen. Daher wird empfohlen, alle statischen Felder in der Enterprise-Bean-Klasse zu verwenden   als endgültig erklärt

Ist es also nicht besser, eine ApplicationScoped Bean zu erstellen, die die Tabellenanwendung ohne statische Felder speichert?

Es hat es ausprobiert und es scheint zu funktionieren ...

Hier ist mein Beispiel:

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

}

-> Sorry für mein Englisch ...


1
2018-06-17 08:42