Несколько сессий для одного сервлета в Java - PullRequest
2 голосов
/ 06 октября 2009

У меня есть один сервлет, обслуживающий несколько сайтов, и поэтому я хочу иметь разные сессии для разных сайтов, даже если один и тот же пользователь.

Есть ли какая-либо поддержка для этого в Java или мне нужно вместо этого использовать префикс имен атрибутов? Я думаю, префикс не очень хорошая идея.

/ Br Johannes

Ответы [ 4 ]

1 голос
/ 06 октября 2009

ЭТО НЕ МОЖЕТ быть сделано в контейнере сервлета только на основе параметров URL; вам придется сделать это самостоятельно. Однако вместо того, чтобы иметь дело с префиксами атрибутов в вашем сервлете, проще всего управлять «отдельными» сеансами через фильтр:

  1. Напишите простой класс-оболочку для HttpSession. Пусть он содержит карту атрибутов и поддерживает все методы атрибута / значения по указанной карте; делегировать все остальные методы для реального сеанса, который вы переносите. Переопределите invalidate() метод, чтобы удалить оболочку вашего сеанса вместо того, чтобы убивать весь «настоящий» сеанс.
  2. Написать фильтр сервлетов; сопоставьте его, чтобы перехватить все применимые URL.
  3. Сохранение коллекции ваших сессионных оболочек в качестве атрибута в реальном сеансе.
  4. В методе doFilter() вашего фильтра извлеките соответствующую оболочку сеанса из коллекции и вставьте ее в запрос, передаваемый по цепочке, обернув исходный запрос в HttpServletRequestWrapper, метод getSession () которого перезаписан.
  5. Ваши сервлеты / JSP / etc ... будут наслаждаться "отдельными" сессиями.

Обратите внимание, что "lastAccessedTime" в сеансах используется совместно с этим подходом. Если вам нужно хранить их отдельно, вам придется написать собственный код для поддержания этого параметра и для истечения срока действия ваших оболочек сессии.

0 голосов
/ 01 сентября 2017

Недавно я тоже сталкивался с этой проблемой и согласился с предложением ChssPly76 решить ее. Я думал, что опубликую свои результаты здесь, чтобы обеспечить справочную реализацию. Он не был тщательно протестирован, поэтому, пожалуйста, дайте мне знать, если вы обнаружите какие-либо недостатки.

Я предполагаю, что каждый запрос к сервлету содержит параметр с именем uiid , который представляет идентификатор пользователя. Запрашивающая сторона должна отслеживать отправку нового идентификатора при каждом нажатии на ссылку, которая открывает новое окно. В моем случае этого достаточно, но вы можете использовать любой другой (возможно, более безопасный) метод здесь. Кроме того, я работаю с Tomcat 7 или 8. Возможно, вам придется расширять другие классы при работе с различными контейнерами сервлетов, но API не должны слишком сильно меняться.

В дальнейшем созданные сеансы называются подсессиями , исходный управляемый сеанс контейнера - это родительский сеанс . Реализация состоит из следующих пяти классов:

SingleSessionManager отслеживает создание, распространение и очистку всех подразделений. Он делает это, действуя как фильтр сервлета, который заменяет ServletRequest оболочкой, которая возвращает соответствующую подсессию. Планировщик периодически проверяет просроченные подсессии ... и да, это одиночка. Извините, но они все еще мне нравятся.

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

}

Подсессия идентифицируется SubSessionKey . Эти ключевые объекты зависят от uiid и идентификатора родительского сеанса.

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 + "]";
    }

}

HttpServletRequestWrapper оборачивает объект HttpServletRequest. Все методы перенаправляются в упакованный запрос, кроме методов getSession, которые будут возвращать HttpSessionWrapper в зависимости от идентификатора пользователя в параметрах этого запроса.

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

HttpSessionWrapper представляет подсессию.

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

}

SessionInvalidator - это HttpSessionListener, который заботится об очистке всех подсекций в случае аннулирования родительского сеанса.

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

}

Включите все, добавив в ваш web.xml следующее 1041 *

<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 голосов
/ 06 октября 2009

Сессия уникальна для комбинации пользователя и веб-приложения. Конечно, вы можете развернуть свой сервлет в нескольких веб-приложениях на одном и том же экземпляре Tomcat, но вы не сможете перенаправить HTTP-запрос к различным веб-приложениям просто на основе параметров URL, если вы не оцените параметры URL во втором сервлете и не перенаправите браузер на новый URL для конкретного веб-приложения.

Разные контейнеры сервлетов или серверы приложений J2EE могут иметь разные опции для маршрутизации запросов к определенным веб-приложениям, но AFAIK сразу после установки Tomcat может делегировать запрос только на основе имени хоста или базового каталога, например ::10000

0 голосов
/ 06 октября 2009

Я думаю, вы ищете что-то вроде Apache Tomcat . Он будет управлять отдельными сессиями для отдельных приложений сервлетов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...