2 Stimmen

Mehrere Sitzungen für ein Servlet in Java

Ich habe ein Servlet, das sich um mehrere Websites kümmert, und daher möchte ich verschiedene Sitzungen für verschiedene Websites haben, auch wenn es derselbe Benutzer ist.

Gibt es irgendeine Unterstützung für diese in Java oder muss ich die Attributnamen stattdessen vorangestellt? Ich denke, Präfixe sind keine gute Idee.

/Br Johannes

0 Stimmen

Kann ich so etwas wie eine manuelle Pfadeinstellung vornehmen, um die Situation zu verbessern?

0 Stimmen

Bitte definieren Sie den Begriff "mehrere Standorte".

0 Stimmen

Ja, ich stimme zu, meine Definition war schlecht :) . Ein und dasselbe Servlet zeigt je nach den verschiedenen URL-Parametern unterschiedliche HTML-Seiten an. Für verschiedene URL-Parameter möchte ich verschiedene Sitzungen haben, auch wenn es derselbe Benutzer ist.

2voto

ChssPly76 Punkte 97241

Dies kann im Servlet-Container NICHT allein auf der Grundlage von URL-Parametern geschehen; Sie müssen es selbst tun. Anstatt sich mit Attribut-Präfixen in Ihrem Servlet zu beschäftigen, ist es jedoch am einfachsten, "getrennte" Sitzungen über Filter zu verwalten:

  1. Schreiben Sie eine einfache Wrapper-Klasse für HttpSession. Lassen Sie es halten eine Karte von Attributen und zurück alle Attribut / Wert-Methoden durch die Karte; delegieren Sie alle anderen Methoden auf die eigentliche Sitzung, die Sie wickeln. Überschreiben Sie invalidate() Methode, um Ihren Sitzungs-Wrapper zu entfernen, anstatt die gesamte "echte" Sitzung zu beenden.
  2. Schreiben Sie einen Servlet-Filter; ordnen Sie ihn zu, um alle zutreffenden URLs abzufangen.
  3. Pflegen Sie eine Sammlung Ihrer Session-Wrapper als Attribut innerhalb der echten Session.
  4. In Ihrem Filter ist doFilter() Methode extrahiert den entsprechenden Sitzungs-Wrapper aus der Sammlung und fügt ihn in die Anforderung ein, die Sie in der Kette weitergeben, indem Sie die ursprüngliche Anforderung in einen HttpServletRequestWrapper einschließen, dessen getSession()-Methode überschrieben wird.
  5. Ihre Servlets / JSPs / etc... werden "separate" Sitzungen genießen.

Beachten Sie, dass die "lastAccessedTime" von Sessions mit diesem Ansatz geteilt wird. Wenn Sie diese getrennt halten wollen, müssen Sie Ihren eigenen Code für die Pflege dieser Einstellung und für das Auslaufen Ihrer Session-Wrapper schreiben.

2voto

user3792852 Punkte 181

Ich bin vor kurzem auch auf dieses Problem gestoßen und habe den Vorschlag von ChssPly76 zur Lösung des Problems aufgegriffen. Ich dachte, ich würde meine Ergebnisse hier posten, um eine Referenzimplementierung bereitzustellen. Es wurde noch nicht ausgiebig getestet, also lassen Sie es mich bitte wissen, wenn Sie irgendwelche Schwachstellen entdecken.

Ich gehe davon aus, dass jede Anfrage an ein Servlet einen Parameter namens uiid die eine Benutzer-ID darstellt. Der Antragsteller muss jedes Mal eine neue ID senden, wenn ein Link angeklickt wird, der ein neues Fenster öffnet. In meinem Fall ist dies ausreichend, aber Sie können auch jede andere (vielleicht sicherere) Methode verwenden. Außerdem arbeite ich mit Tomcat 7 oder 8. Wenn Sie mit anderen Servlet-Containern arbeiten, müssen Sie möglicherweise andere Klassen erweitern, aber die APIs sollten sich nicht allzu sehr ändern.

Im Folgenden werden die erstellten Sitzungen als Teilbereiche ist die ursprüngliche containerverwaltete Sitzung die Elternsitzung . Die Implementierung besteht aus den folgenden fünf Klassen:

Le site SingleSessionManager behält den Überblick über die Erstellung, Verteilung und Bereinigung aller Unterabschnitte. Dazu fungiert er als Servlet-Filter, der die ServletRequest durch einen Wrapper ersetzt, der die entsprechende Subsession zurückgibt. Ein Scheduler prüft periodisch, ob die Subsessions abgelaufen sind ... und ja, es ist ein Singleton. Tut mir leid, aber ich mag sie trotzdem.

package session;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

/**
 * A singleton class that manages multiple sessions on top of a regular container managed session.
 * See web.xml for information on how to enable this.
 *
 */
public class SingleSessionManager implements Filter {

    /**
     * The default session timeout in seconds to be used if no explicit timeout is provided.
     */
    public static final int DEFAULT_TIMEOUT = 900;

    /**
     * The default interval for session validation checks in seconds to be used if no explicit
     * timeout is provided.
     */
    public static final int DEFAULT_SESSION_INVALIDATION_CHECK = 15;

    private static SingleSessionManager instance;

    private ScheduledExecutorService scheduler;
    protected int timeout;
    protected long sessionInvalidationCheck;

    private Map<SubSessionKey, HttpSessionWrapper> sessions = new ConcurrentHashMap<SubSessionKey, HttpSessionWrapper>();

    public SingleSessionManager() {
        sessionInvalidationCheck = DEFAULT_SESSION_INVALIDATION_CHECK;
        timeout = DEFAULT_TIMEOUT;
    }

    public static SingleSessionManager getInstance() {
        if (instance == null) {
            instance = new SingleSessionManager();
        }
        return instance;
    }

    @Override
    public void destroy() {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequestWrapper wrapper = new HttpServletRequestWrapper((HttpServletRequest) request);
        chain.doFilter(wrapper,  response);
    }

    @Override
    public void init(FilterConfig cfg) throws ServletException {
        String timeout = cfg.getInitParameter("sessionTimeout");
        if (timeout != null && !timeout.trim().equals("")) {
            getInstance().timeout = Integer.parseInt(timeout) * 60;
        }

        String sessionInvalidationCheck = cfg.getInitParameter("sessionInvalidationCheck");
        if (sessionInvalidationCheck != null && !sessionInvalidationCheck.trim().equals("")) {
            getInstance().sessionInvalidationCheck = Long.parseLong(sessionInvalidationCheck);
        }

        getInstance().startSessionExpirationScheduler();
    }

    /**
     * Create a new session ID.
     * 
     * @return A new unique session ID.
     */
    public String generateSessionId() {
        return UUID.randomUUID().toString();
    }

    protected void startSessionExpirationScheduler() {
        if (scheduler == null) {
            scheduler = Executors.newScheduledThreadPool(1);
            final Runnable sessionInvalidator = new Runnable() {
                public void run() {
                    SingleSessionManager.getInstance().destroyExpiredSessions();
                }
            };
            final ScheduledFuture<?> sessionInvalidatorHandle =
                    scheduler.scheduleAtFixedRate(sessionInvalidator
                            , this.sessionInvalidationCheck
                            , this.sessionInvalidationCheck
                            , TimeUnit.SECONDS);
        }
    }

    /**
     * Get the timeout after which a session will be invalidated.
     * 
     * @return The timeout of a session in seconds.
     */
    public int getSessionTimeout() {
        return timeout;
    }

    /**
     * Retrieve a session.
     * 
     * @param uiid
     *            The user id this session is to be associated with.
     * @param create
     *            If <code>true</code> and no session exists for the given user id, a new session is
     *            created and associated with the given user id. If <code>false</code> and no
     *            session exists for the given user id, no new session will be created and this
     *            method will return <code>null</code>.
     * @param originalSession
     *            The original backing session created and managed by the servlet container.
     * @return The session associated with the given user id if this session exists and/or create is
     *         set to <code>true</code>, <code>null</code> otherwise.
     */
    public HttpSession getSession(String uiid, boolean create, HttpSession originalSession) {
        if (uiid != null) {
            SubSessionKey key = new SubSessionKey(originalSession.getId(), uiid);
            if (!sessions.containsKey(key) && create) {
                HttpSessionWrapper sw = new HttpSessionWrapper(uiid, originalSession);
                sessions.put(key, sw);
            }
            HttpSessionWrapper session = sessions.get(key);
            session.setLastAccessedTime(System.currentTimeMillis());
            return session;
        }
        return null;
    }

    public HttpSessionWrapper removeSession(SubSessionKey key) {
        return sessions.remove(key);
    }

    /**
     * Destroy a session, freeing all it's resources.
     * 
     * @param session
     *            The session to be destroyed.
     */
    public void destroySession(HttpSessionWrapper session) {
        String uiid = ((HttpSessionWrapper)session).getUiid();
        SubSessionKey key = new SubSessionKey(session.getOriginalSession().getId(), uiid);
        HttpSessionWrapper w = getInstance().removeSession(key);
        if (w != null) {
            System.out.println("Session " + w.getId() + " with uiid " + uiid + " was destroyed.");
        } else {
            System.out.println("uiid " + uiid + " does not have a session.");
        }
    }

    /**
     * Destroy all session that are expired at the time of this method call.
     */
    public void destroyExpiredSessions() {
        List<HttpSessionWrapper> markedForDelete = new ArrayList<HttpSessionWrapper>();
        long time = System.currentTimeMillis() / 1000;
        for (HttpSessionWrapper session : sessions.values()) {
            if (time - (session.getLastAccessedTime() / 1000) >= session.getMaxInactiveInterval()) {
                markedForDelete.add(session);
            }
        }
        for (HttpSessionWrapper session : markedForDelete) {
            destroySession(session);
        }
    }

    /**
     * Remove all subsessions that were created from a given parent session.
     * 
     * @param originalSession
     *            All subsessions created with this session as their parent session will be
     *            invalidated.
     */
    public void clearAllSessions(HttpSession originalSession) {
        Iterator<HttpSessionWrapper> it = sessions.values().iterator();
        while (it.hasNext()) {
            HttpSessionWrapper w = it.next();
            if (w.getOriginalSession().getId().equals(originalSession.getId())) {
                destroySession(w);
            }
        }
    }

    public void setSessionTimeout(int timeout) {
        this.timeout = timeout;
    }

}

Eine Untersitzung wird durch ein SubSessionKey . Diese Schlüsselobjekte hängen von der uiid und der ID der übergeordneten Sitzung ab.

package session;

/**
 * Key object for identifying a subsession.
 *
 */
public class SubSessionKey {

    private String sessionId;
    private String uiid;

    /**
     * Create a new instance of {@link SubSessionKey}.
     * 
     * @param sessionId
     *            The session id of the parent session.
     * @param uiid
     *            The users's id this session is associated with.
     */
    public SubSessionKey(String sessionId, String uiid) {
        super();
        this.sessionId = sessionId;
        this.uiid = uiid;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((sessionId == null) ? 0 : sessionId.hashCode());
        result = prime * result + ((uiid == null) ? 0 : uiid.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        SubSessionKey other = (SubSessionKey) obj;
        if (sessionId == null) {
            if (other.sessionId != null)
                return false;
        } else if (!sessionId.equals(other.sessionId))
            return false;
        if (uiid == null) {
            if (other.uiid != null)
                return false;
        } else if (!uiid.equals(other.uiid))
            return false;
        return true;
    }

    @Override
    public String toString() {
        return "SubSessionKey [sessionId=" + sessionId + ", uiid=" + uiid + "]";
    }

}

Le site HttpServletRequestWrapper umhüllt ein HttpServletRequest-Objekt. Alle Methoden werden auf die verpackte Anfrage umgeleitet, mit Ausnahme der Methode getSession Methoden, die eine HttpSessionWrapper abhängig von der Benutzer-ID in den Parametern dieser Anfrage.

package session;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

/**
 * Wrapper class that wraps a {@link HttpServletRequest} object. All methods are redirected to the
 * wrapped request except for the <code>getSession</code> which will return an
 * {@link HttpSessionWrapper} depending on the user id in this request's parameters.
 *
 */
public class HttpServletRequestWrapper extends javax.servlet.http.HttpServletRequestWrapper {

    private HttpServletRequest req;

    public HttpServletRequestWrapper(HttpServletRequest req) {
        super(req);
        this.req = req;
    }

    @Override
    public HttpSession getSession() {
        return getSession(true);
    }

    @Override
    public HttpSession getSession(boolean create) {
        String[] uiid = getParameterMap().get("uiid");
        if (uiid != null && uiid.length >= 1) {
            return SingleSessionManager.getInstance().getSession(uiid[0], create, req.getSession(create));
        }
        return req.getSession(create);
    }
}

Le site HttpSessionWrapper steht für eine Subsession.

package session;

import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionContext;

/**
 * Implementation of a HttpSession. Each instance of this class is created around a container
 * managed parent session with it's lifetime linked to it's parent's.
 *
 */
@SuppressWarnings("deprecation")
public class HttpSessionWrapper implements HttpSession {

    private Map<String, Object> attributes;
    private Map<String, Object> values;
    private long creationTime;
    private String id;
    private String uiid;
    private boolean isNew;
    private long lastAccessedTime;
    private HttpSession originalSession;

    public HttpSessionWrapper(String uiid, HttpSession originalSession) {
        creationTime = System.currentTimeMillis();
        lastAccessedTime = creationTime;
        id = SingleSessionManager.getInstance().generateSessionId();
        isNew = true;
        attributes = new HashMap<String, Object>();
        Enumeration<String> names = originalSession.getAttributeNames();
        while (names.hasMoreElements()) {
            String name = names.nextElement();
            attributes.put(name, originalSession.getAttribute(name));
        }
        values = new HashMap<String, Object>();
        for (String name : originalSession.getValueNames()) {
            values.put(name, originalSession.getValue(name));
        }
        this.uiid = uiid;
        this.originalSession = originalSession;
    }

    public String getUiid() {
        return uiid;
    }

    public void setNew(boolean b) {
        isNew = b;
    }

    public void setLastAccessedTime(long time) {
        lastAccessedTime = time;
    }

    @Override
    public Object getAttribute(String arg0) {
        return attributes.get(arg0);
    }

    @Override
    public Enumeration<String> getAttributeNames() {
        return Collections.enumeration(attributes.keySet());
    }

    @Override
    public long getCreationTime() {
        return creationTime;
    }

    @Override
    public String getId() {
        return id;
    }

    @Override
    public long getLastAccessedTime() {
        return lastAccessedTime;
    }

    @Override
    public int getMaxInactiveInterval() {
        return SingleSessionManager.getInstance().getSessionTimeout();
    }

    @Override
    public ServletContext getServletContext() {
        return originalSession.getServletContext();
    }

    @Override
    public HttpSessionContext getSessionContext() {
        return new HttpSessionContext() {

            @Override
            public Enumeration<String> getIds() {
                return Collections.enumeration(new HashSet<String>());
            }

            @Override
            public HttpSession getSession(String arg0) {
                return null;
            }

        };
    }

    @Override
    public Object getValue(String arg0) {
        return values.get(arg0);
    }

    @Override
    public String[] getValueNames() {
        return values.keySet().toArray(new String[values.size()]);
    }

    @Override
    public void invalidate() {
        SingleSessionManager.getInstance().destroySession(this);
    }

    @Override
    public boolean isNew() {
        return isNew;
    }

    @Override
    public void putValue(String arg0, Object arg1) {
        values.put(arg0, arg1);
    }

    @Override
    public void removeAttribute(String arg0) {
        attributes.remove(arg0);
    }

    @Override
    public void removeValue(String arg0) {
        values.remove(arg0);
    }

    @Override
    public void setAttribute(String arg0, Object arg1) {
        attributes.put(arg0, arg1);
    }

    @Override
    public void setMaxInactiveInterval(int arg0) {
        SingleSessionManager.getInstance().setSessionTimeout(arg0);
    }

    public HttpSession getOriginalSession() {
        return originalSession;
    }

}

Le site SessionInvalidator ist ein HttpSessionListener die dafür sorgt, dass alle Untersitzungen bereinigt werden, wenn ihre übergeordnete Sitzung ungültig wird.

package session;

import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

/**
 * Session listener that listens for the destruction of a container managed session and takes care
 * of destroying all it's subsessions.
 * <p>
 * Normally this listener won't have much to do since subsessions usually have a shorter lifetime
 * than their parent session and therefore will timeout long before this method is called. This
 * listener will only be important in case of an explicit invalidation of a parent session.
 * </p>
 *
 */
public class SessionInvalidator implements HttpSessionListener {

    @Override
    public void sessionCreated(HttpSessionEvent arg0) {
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent arg0) {
        SingleSessionManager.getInstance().clearAllSessions(arg0.getSession());
    }

}

Aktivieren Sie alles, indem Sie Folgendes in Ihre web.xml einfügen

<filter>
  <filter-name>SingleSessionFilter</filter-name>
  <filter-class>de.supportgis.sgjWeb.session.SingleSessionManager</filter-class>
  <!-- The timeout in minutes after which a subsession will be invalidated. It is recommended to set a session timeout for the servled container using the parameter "session-timeout", which is higher than this value. -->
  <init-param>
    <param-name>sessionTimeout</param-name>
    <param-value>1</param-value>
  </init-param>
  <init-param>
    <!-- The intervall in seconds in which a check for expired sessions will be performed. -->
    <param-name>sessionInvalidationCheck</param-name>
    <param-value>15</param-value>
  </init-param>
</filter>
<filter-mapping>
  <filter-name>SingleSessionFilter</filter-name>
  <!-- Insert the name of your servlet here to which the session management should apply, or use url-pattern instead. --> 
  <servlet-name>YourServlet</servlet-name>
</filter-mapping>
<listener>
  <listener-class>session.SessionInvalidator</listener-class>
</listener>

<!-- Timeout of the parent session -->
<session-config>
  <session-timeout>40</session-timeout>
  <!-- Session timeout interval in minutes -->
</session-config>

0 Stimmen

Ich habe mit einigen Fehlerbehebungen geantwortet

0voto

Peter Di Cecco Punkte 1518

Ich denke, Sie suchen nach etwas wie Apache Tomcat . Es verwaltet einzelne Sitzungen für einzelne Servlet-Anwendungen.

0 Stimmen

Ja, ich verwende Tomcat. Das gleiche Servlet zeigt verschiedene HTML-Seiten an, abhängig von verschiedenen URL-Parametern. Für verschiedene URL-Parameter möchte ich verschiedene Sitzungen haben, auch wenn es derselbe Benutzer ist.

0 Stimmen

@Johannes : Wie sind Sie mit dieser Situation umgegangen. Ich bin in der gleichen Situation wie du 2 Jahre zuvor.... :'(

0voto

Robert Sutton Punkte 465

Hier ist eine Fehlerbehebung für die Antwort von Benutzer3792852

public HttpSession getSession(String uiid, boolean create, HttpSession originalSession)
{
    if (uiid != null && originalSession != null)
    {
        SubSessionKey key = new SubSessionKey(originalSession.getId(), uiid);
        synchronized (sessions)
        {
            HttpSessionWrapper session = sessions.get(key);
            if (session == null && create)
            {
                session = new HttpSessionWrapper(uiid, originalSession);
                sessions.put(key, session);
            }
            if (session != null)
            {
                session.setLastAccessedTime(System.currentTimeMillis());
            }
            return session;
        }
    }
    return null;
}

0voto

jarnbjo Punkte 33136

Die Sitzung ist einmalig für eine Kombination aus Benutzer und Webanwendung. Sie können Ihr Servlet natürlich in mehreren Webanwendungen auf derselben Tomcat-Instanz einsetzen, aber Sie werden nicht in der Lage sein, die HTTP-Anfrage einfach auf der Grundlage von URL-Parametern an verschiedene Webanwendungen weiterzuleiten, es sei denn, Sie werten die URL-Parameter in einem zweiten Servlet aus und leiten den Browser an eine neue URL für die spezifische Webanwendung weiter.

Verschiedene Servlet-Container oder J2EE-App-Server haben möglicherweise unterschiedliche Optionen für die Weiterleitung von Anfragen an bestimmte Webanwendungen, aber soweit ich weiß, kann Tomcat die Anfrage nur auf der Grundlage des Hostnamens oder des Basisverzeichnisses weiterleiten, z. B.:

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X