Подтвердить что ты не робот

Доступ к HttpSession из HttpServletRequest в веб-узле @ServerEndpoint

Можно ли получить HttpServletRequest внутри @ServerEndpoint? Прежде всего, я пытаюсь получить его, чтобы получить доступ к объекту HttpSession.

4b9b3361

Ответ 1

Обновление (ноябрь 2016 г.). Информация, представленная в этом ответе, предназначена для спецификации JSR356, отдельные реализации спецификации могут отличаться вне этой информации. Другие предложения, найденные в комментариях и других ответах, - это все специфические для реализации действия вне спецификации JSR356.

Если предложения здесь вызывают проблемы, обновите различные установки Jetty, Tomcat, Wildfly или Glassfish/Tyrus. Сообщалось, что все текущие версии этих реализаций работают так, как описано ниже.

Теперь вернемся к исходному ответу от Август 2013...

Ответ Мартина Андерсона имеет недостаток concurrency. Конфигуратор может быть вызван несколькими потоками одновременно, вероятно, у вас не будет доступа к правильному объекту HttpSession между вызовами из modifyHandshake() и getEndpointInstance().

Или сказал другой путь...

  • Запрос A
  • Изменить рукопожатие A
  • Запрос B
  • Изменить рукопожатие B
  • Получить экземпляр конечной точки A < - это будет запрос B HttpSession
  • Получить экземпляр конечной точки B

Здесь модификация кода Мартина, использующая карту ServerEndpointConfig.getUserProperties(), чтобы сделать HttpSession доступным для вашего экземпляра сокета во время вызова метода @OnOpen

GetHttpSessionConfigurator.java

package examples;

import javax.servlet.http.HttpSession;
import javax.websocket.HandshakeResponse;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpointConfig;

public class GetHttpSessionConfigurator extends ServerEndpointConfig.Configurator
{
    @Override
    public void modifyHandshake(ServerEndpointConfig config, 
                                HandshakeRequest request, 
                                HandshakeResponse response)
    {
        HttpSession httpSession = (HttpSession)request.getHttpSession();
        config.getUserProperties().put(HttpSession.class.getName(),httpSession);
    }
}

GetHttpSessionSocket.java

package examples;

import java.io.IOException;

import javax.servlet.http.HttpSession;
import javax.websocket.EndpointConfig;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint(value = "/example", 
                configurator = GetHttpSessionConfigurator.class)
public class GetHttpSessionSocket
{
    private Session wsSession;
    private HttpSession httpSession;

    @OnOpen
    public void open(Session session, EndpointConfig config) {
        this.wsSession = session;
        this.httpSession = (HttpSession) config.getUserProperties()
                                           .get(HttpSession.class.getName());
    }

    @OnMessage
    public void echo(String msg) throws IOException {
        wsSession.getBasicRemote().sendText(msg);
    }
}

Бонусная функция: нет instanceof или требуется литье.

Некоторые сведения о EndpointConfig

EndpointConfig объекты существуют на "экземпляр конечной точки".

Однако "Экземпляр конечной точки" имеет 2 значения со спецификацией.

  • Поведение по умолчанию JSR, где каждый входящий запрос на обновление приводит к экземпляру нового объекта класса конечной точки
  • A javax.websocket.Session, который связывает экземпляр конечной точки объекта с его конфигурацией с определенным логическим соединением.

Можно использовать экземпляр Endpointpoint Singleton для нескольких экземпляров javax.websocket.Session (это одна из функций, поддерживаемых ServerEndpointConfig.Configurator)

Реализация ServerContainer будет отслеживать набор ServerEndpointConfig, который будет представлять все развернутые конечные точки, на которые сервер может отвечать на запрос обновления websocket.

Эти экземпляры экземпляра ServerEndpointConfig могут исходить из нескольких разных источников.

  • Вручную предоставляется javax.websocket.server.ServerContainer.addEndpoint(ServerEndpointConfig)
    • Обычно выполняется при вызове javax.servlet.ServletContextInitializer.contextInitialized(ServletContextEvent sce)
  • От вызова javax.websocket.server.ServerApplicationConfig.getEndpointConfigs(Set).
  • Автоматически создается при сканировании веб-приложения для аннотированных классов @ServerEndpoint.

Эти экземпляры объектов ServerEndpointConfig существуют как значения по умолчанию, когда в конце создается javax.websocket.Session.

Экземпляр ServerEndpointConfig.Configurator

Прежде чем какие-либо запросы на обновление будут получены или обработаны, все объекты ServerEndpointConfig.Configurator теперь существуют и готовы выполнить свою основную и единственную цель, чтобы обеспечить возможность настройки процесса обновления соединения веб-соединения с возможным javax.websocket.Session

Доступ к определенному сеансу EndpointConfig

Обратите внимание: вы не можете получить доступ к экземплярам объекта ServerEndpointConfig из экземпляра конечной точки. Вы можете получить доступ только к экземплярам EndpointConfig.

Это означает, что если вы предоставили ServerContainer.addEndpoint(new MyCustomServerEndpointConfig()) во время развертывания и позже попытались получить к нему доступ через аннотации, это не сработает.

Все следующие недопустимы.

@OnOpen
public void onOpen(Session session, EndpointConfig config)
{
    MyCustomServerEndpointConfig myconfig = (MyCustomServerEndpointConfig) config;
    /* this would fail as the config is cannot be cast around like that */
}

// --- or ---

@OnOpen
public void onOpen(Session session, ServerEndpointConfig config)
{
    /* For @OnOpen, the websocket implementation would assume
       that the ServerEndpointConfig to be a declared PathParam
     */
}

// --- or ---

@OnOpen
public void onOpen(Session session, MyCustomServerEndpointConfig config)
{
    /* Again, for @OnOpen, the websocket implementation would assume
       that the MyCustomServerEndpointConfig to be a declared PathParam
     */
}

Вы можете получить доступ к EndpointConfig в течение срока действия экземпляра объекта Endpoint, но в течение ограниченного времени. javax.websocket.Endpoint.onOpen(Session,Endpoint), аннотированные методы @OnOpen или с использованием CDI. EndpointConfig недоступен каким-либо другим способом или в любое другое время.

Однако вы всегда можете получить доступ к UserProperties с помощью вызова Session.getUserProperties(), который всегда доступен. Эта карта свойств пользователя всегда доступна, будь то с помощью аннотированных методов (таких как параметр сеанса во время вызовов @OnOpen, @OnClose, @OnError или @OnMessage), посредством CDI-инъекции сеанса или даже с помощью использование не аннотированных веб-сайтов, которые простираются от javax.websocket.Endpoint.

Как работать с обновлением

Как указано выше, каждая из определенных конечных точек будет иметь связанный с ней ServerEndpointConfig.

Те ServerEndpointConfigs представляют собой один экземпляр, который представляет состояние по умолчанию EndpointConfig, которое в конечном итоге становится доступным для экземпляров конечной точки, которые возможно и в конечном итоге созданы.

Когда поступает входящий запрос на обновление, он выполняет следующие действия в JSR.

  • соответствует ли путь любому элементу ServerEndpointConfig.getPath()
    • Если нет совпадения, верните 404 для обновления
  • передать запрос на обновление в ServerEndpointConfig.Configurator.checkOrigin()
    • Если недействительно, возвращайте ошибку для обновления ответа
    • создать HandshakeResponse
  • передать запрос на обновление в ServerEndpointConfig.Configurator.getNegotiatedSubprotocol()
    • сохранить ответ в HandshakeResponse
  • передать запрос на обновление в ServerEndpointConfig.Configurator.getNegotiatedExtensions()
    • сохранить ответ в HandshakeResponse
  • Создайте новый объект ServerEndpointConfig с конечной точкой. копировать кодировщики, декодеры и пользовательские свойства. Этот новый ServerEndpointConfig обертывает по умолчанию путь, расширения, класс конечной точки, подпрограммы, конфигуратор.
  • передать запрос обновления, ответ и новый ServerEndpointConfig в ServerEndpointConfig.Configurator.modifyHandshake()
  • вызов ServerEndpointConfig.getEndpointClass()
  • использовать класс на ServerEndpointConfig.Configurator.getEndpointInstance(класс)
  • создать сеанс, связать экземпляр конечной точки и объект EndpointConfig.
  • Информировать конечный пункт подключения
  • аннотированные методы, которые хотят, чтобы EndpointConfig получил тот, который связан с этим сеансом.
  • вызывает Session.getUserProperties() возвращает EndpointConfig.getUserProperties()

Следует отметить, что ServerEndpointConfig.Configurator является одноточечным, в каждой отображаемой конечной точке ServerContainer.

Это намеренно и желательно, чтобы позволить разработчикам несколько функций.

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

Если реализации создали новый конфигуратор для каждого рукопожатия, этот метод не был бы возможен.

(Раскрытие: я пишу и поддерживаю реализацию JSR-356 для Jetty 9)

Ответ 2

Введение

Неясно, хотите ли вы HttpServletRequest, HttpSession или свойства из HttpSession. Мой ответ покажет, как получить HttpSession или отдельные свойства.

Я пропустил проверки ограничений по нулям и индексам для краткости.

Предостережения

Это сложно. Ответ Мартина Андерсона неверен, потому что для каждого соединения используется один и тот же экземпляр ServerEndpointConfig.Configurator, поэтому существует условие расы. Хотя документы указывают, что "реализация создает новый экземпляр конфигуратора для каждой логической конечной точки", спецификация явно не определяет "логическую конечную точку". На основе контекста всех мест, где используется эта фраза, это означает привязку класса, конфигуратора, пути и других параметров, то есть a ServerEndpointConfig, который явно разделяется. В любом случае вы можете легко увидеть, использует ли реализация один и тот же экземпляр, распечатав его toString() из modifyHandshake(...).

Более удивительно, что ответ от Joakim Erdfelt также не работает надежно. В тексте JSR 356 не упоминается EndpointConfig.getUserProperties(), это только в JavaDoc, и нигде не кажется, что он определен, каково его точное отношение к Session.getUserProperties(). На практике некоторые реализации (например, Glassfish) возвращают один и тот же экземпляр Map для всех вызовов ServerEndpointConfig.getUserProperties(), в то время как другие (например, Tomcat 8) этого не делают. Вы можете проверить, распечатав содержимое карты, прежде чем изменять его в modifyHandshake(...).

Чтобы проверить, я скопировал код непосредственно из других ответов, а затем протестировал его на многопоточном клиенте, который я написал. В обоих случаях я заметил, что неправильный сеанс связан с экземпляром конечной точки.

Структура решений

Я разработал два решения, которые я проверил, работая правильно при тестировании с многопоточным клиентом. Есть два ключевых трюка.

Сначала используйте фильтр с тем же путем, что и WebSocket. Это даст вам доступ к HttpServletRequest и HttpSession. Это также дает вам возможность создать сеанс, если он еще не существует (хотя в этом случае использование HTTP-сеанса вообще кажется сомнительным).

Во-вторых, найдите некоторые свойства, которые существуют как в WebSocket Session, так и HttpServletRequest или HttpSession. Оказывается, есть два кандидата: getUserPrincipal() и getRequestParameterMap(). Я покажу вам, как злоупотреблять обоими из них:)

Решение с использованием принципала пользователя

Самый простой способ - использовать Session.getUserPrincipal() и HttpServletRequest.getUserPrincipal(). Недостатком является то, что это может помешать другим законным видам использования этого свойства, поэтому используйте его, только если вы готовы к этим последствиям.

Если вы хотите сохранить только одну строку, например идентификатор пользователя, на самом деле это не слишком много злоупотреблений, хотя, по-видимому, это должно быть установлено каким-то образом управляемым контейнером, а не переопределять оболочку, как я покажу вам, В любом случае, вы сделали бы это, просто переопределив Principal.getName(). Тогда вам даже не нужно бросать его в Endpoint. Но если вы можете погладить его, вы также можете передать весь объект HttpSession следующим образом.

PrincipalWithSession.java

package example1;

import java.security.Principal;
import javax.servlet.http.HttpSession;

public class PrincipalWithSession implements Principal {
    private final HttpSession session;

    public PrincipalWithSession(HttpSession session) {
        this.session = session;
    }

    public HttpSession getSession() {
        return session;
    }

    @Override
    public String getName() {
        return ""; // whatever is appropriate for your app, e.g., user ID
    }
}

WebSocketFilter.java

package example1;

import java.io.IOException;
import java.security.Principal;
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.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

@WebFilter("/example1")
public class WebSocketFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        final PrincipalWithSession p = new PrincipalWithSession(httpRequest.getSession());
        HttpServletRequestWrapper wrappedRequest = new HttpServletRequestWrapper(httpRequest) {
            @Override
            public Principal getUserPrincipal() {
                return p;
            }
        };
        chain.doFilter(wrappedRequest, response);
    }

    public void init(FilterConfig config) throws ServletException { }
    public void destroy() { }
}

WebSocketEndpoint.java

package example1;

import javax.servlet.http.HttpSession;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint("/example1")
public class WebSocketEndpoint {
    private HttpSession httpSession;

    @OnOpen
    public void onOpen(Session webSocketSession) {
        httpSession = ((PrincipalWithSession) webSocketSession.getUserPrincipal()).getSession();
    }

    @OnMessage
    public String demo(String msg) {
        return msg + "; (example 1) session ID " + httpSession.getId();
    }
}

Решение с использованием параметров запроса

Второй вариант использует Session.getRequestParameterMap() и HttpServletRequest.getParameterMap(). Обратите внимание, что он использует ServerEndpointConfig.getUserProperties(), но в этом случае он безопасен, потому что мы всегда помещаем один и тот же объект в карту, поэтому независимо от того, имеет ли он общий доступ, нет никакой разницы. Уникальный идентификатор сеанса передается не через пользовательские параметры, а через параметры запроса, которые уникальны для каждого запроса.

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

SessionTracker.java

/* A simple, typical, general-purpose servlet session tracker */
package example2;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

@WebListener
public class SessionTracker implements ServletContextListener, HttpSessionListener {
    private final ConcurrentMap<String, HttpSession> sessions = new ConcurrentHashMap<>();

    @Override
    public void contextInitialized(ServletContextEvent event) {
        event.getServletContext().setAttribute(getClass().getName(), this);
    }

    @Override
    public void contextDestroyed(ServletContextEvent event) {
    }

    @Override
    public void sessionCreated(HttpSessionEvent event) {
        sessions.put(event.getSession().getId(), event.getSession());
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent event) {
        sessions.remove(event.getSession().getId());
    }

    public HttpSession getSessionById(String id) {
        return sessions.get(id);
    }
}

WebSocketFilter.java

package example2;

import java.io.IOException;
import java.util.Collections;
import java.util.Map;
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.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

@WebFilter("/example2")
public class WebSocketFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        final Map<String, String[]> fakedParams = Collections.singletonMap("sessionId",
                new String[] { httpRequest.getSession().getId() });
        HttpServletRequestWrapper wrappedRequest = new HttpServletRequestWrapper(httpRequest) {
            @Override
            public Map<String, String[]> getParameterMap() {
                return fakedParams;
            }
        };
        chain.doFilter(wrappedRequest, response);
    }

    @Override
    public void init(FilterConfig config) throws ServletException { }
    @Override
    public void destroy() { }
}

WebSocketEndpoint.java

package example2;

import javax.servlet.http.HttpSession;
import javax.websocket.EndpointConfig;
import javax.websocket.HandshakeResponse;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpoint;
import javax.websocket.server.ServerEndpointConfig;

@ServerEndpoint(value = "/example2", configurator = WebSocketEndpoint.Configurator.class)
public class WebSocketEndpoint {
    private HttpSession httpSession;

    @OnOpen
    public void onOpen(Session webSocketSession, EndpointConfig config) {
        String sessionId = webSocketSession.getRequestParameterMap().get("sessionId").get(0);
        SessionTracker tracker =
                (SessionTracker) config.getUserProperties().get(SessionTracker.class.getName());
        httpSession = tracker.getSessionById(sessionId);
    }

    @OnMessage
    public String demo(String msg) {
        return msg + "; (example 2) session ID " + httpSession.getId();
    }

    public static class Configurator extends ServerEndpointConfig.Configurator {
        @Override
        public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request,
                HandshakeResponse response) {
            Object tracker = ((HttpSession) request.getHttpSession()).getServletContext().getAttribute(
                    SessionTracker.class.getName());
            // This is safe to do because it the same instance of SessionTracker all the time
            sec.getUserProperties().put(SessionTracker.class.getName(), tracker);
            super.modifyHandshake(sec, request, response);
        }
    }
}

Решение для отдельных свойств

Если вам нужны только определенные свойства из HttpSession, а не всего HttpSession, как, например, идентификатор пользователя, тогда вы можете избавиться от всего бизнеса SessionTracker и просто поместить необходимые параметры в вы вернетесь из своего переопределения HttpServletRequestWrapper.getParameterMap(). Затем вы также можете избавиться от пользовательского Configurator; ваши свойства будут удобно доступны из Session.getRequestParameterMap() в конечной точке.

WebSocketFilter.java

package example5;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
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.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

@WebFilter("/example5")
public class WebSocketFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        final Map<String, String[]> props = new HashMap<>();
        // Add properties of interest from session; session ID
        // is just for example
        props.put("sessionId", new String[] { httpRequest.getSession().getId() });
        HttpServletRequestWrapper wrappedRequest = new HttpServletRequestWrapper(httpRequest) {
            @Override
            public Map<String, String[]> getParameterMap() {
                return props;
            }
        };
        chain.doFilter(wrappedRequest, response);
    }

    @Override
    public void destroy() {
    }

    @Override
    public void init(FilterConfig arg0) throws ServletException {
    }
}

WebSocketEndpoint.java

package example5;

import java.util.List;
import java.util.Map;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint("/example5")
public class WebSocketEndpoint {
    private Map<String, List<String>> params;

    @OnOpen
    public void onOpen(Session session) {
        params = session.getRequestParameterMap();
    }

    @OnMessage
    public String demo(String msg) {
        return msg + "; (example 5) session ID " + params.get("sessionId").get(0);
    }
}

Ответ 3

Возможно ли это?

Просмотрите спецификацию API Java для WebSocket, чтобы узнать, возможно ли получение объекта HttpSession. Описание указано на стр. 29:

Так как соединения websocket инициируются с помощью HTTP-запроса, существует связь между HttpSession, под которым клиент и любые веб-узлы, которые установлены в пределах этого HttpSession. API позволяет получить доступ к открытому рукопожатию уникальный HttpSession, соответствующий этому же клиенту.

Так что да, возможно.

Однако, я не думаю, что вы можете получить ссылку на объект HttpServletRequest. Вы можете прослушивать новые запросы сервлета all, используя ServletRequestListener, но вам все равно придется выяснить, какой запрос принадлежат к какой конечной точке сервера. Пожалуйста, дайте мне знать, если вы найдете решение!

Абстрактное руководство пользователя

How-to свободно описывается на страницах 13 и 14 в спецификации и приведено в качестве примера в коде под следующим заголовком.

На английском языке нам нужно перехватить процесс рукопожатия, чтобы получить объект HttpSession. Чтобы затем передать ссылку HttpSession на нашу конечную точку сервера, нам также необходимо перехватить, когда контейнер создает экземпляр конечной точки сервера и вручную вводит ссылку. Мы делаем все это, предоставляя наш собственный ServerEndpointConfig.Configurator и переопределяя методы modifyHandshake() и getEndpointInstance().

Пользовательский конфигуратор будет создан один раз для каждого логического ServerEndpoint (см. JavaDoc).

Пример кода

Это класс конечных точек сервера (я предоставляю реализацию класса CustomConfigurator после этого фрагмента кода):

@ServerEndpoint(value = "/myserverendpoint", configurator = CustomConfigurator.class)
public class MyServerEndpoint
{
    private HttpSession httpSession;

    public void setHttpSession(HttpSession httpSession) {
        if (this.httpSession != null) {
            throw new IllegalStateException("HttpSession has already been set!");
        }

        this.httpSession = httpSession;
    }

    @OnOpen
    public void onOpen(Session session, EndpointConfig config) {
        System.out.println("My Session Id: " + httpSession.getId());
    }
}

И это настраиваемый конфигуратор:

public class CustomConfigurator extends ServerEndpointConfig.Configurator
{
    private HttpSession httpSession;

    // modifyHandshake() is called before getEndpointInstance()!
    @Override
    public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
        httpSession = (HttpSession) request.getHttpSession();
        super.modifyHandshake(sec, request, response);
    }

    @Override
    public <T> T getEndpointInstance(Class<T> endpointClass) throws InstantiationException {
        T endpoint = super.getEndpointInstance(endpointClass);

        if (endpoint instanceof MyServerEndpoint) {
            // The injection point:
            ((MyServerEndpoint) endpoint).setHttpSession(httpSession);
        }
        else {
            throw new InstantiationException(
                    MessageFormat.format("Expected instanceof \"{0}\". Got instanceof \"{1}\".",
                    MyServerEndpoint.class, endpoint.getClass()));
        }

        return endpoint;
    }
}

Ответ 4

Все вышеприведенные ответы стоит прочитать, но ни одна из них не решает проблему OP (и моей).

Вы можете получить доступ к HttpSession при открытии конечной точки WS и передать ее во вновь созданный экземпляр конечной точки, но никто не гарантирует наличие экземпляра HttpSession!

Итак, нам нужен шаг 0 до этого взлома (я ненавижу реализацию JSR 365 WebSocket). Websocket - httpSession возвращает null

Ответ 5

Все возможные решения основаны на:

а. Реализации клиентского браузера поддерживает идентификатор сеанса через значение Cookie, переданное в качестве заголовка HTTP, или (если файлы cookie отключены), он управляется контейнером Servlet, который генерирует постфикс Session ID для сгенерированных URL

В. Вы можете получить доступ к заголовкам HTTP-запросов только во время рукопожатия HTTP; после этого это протокол Websocket

Итак, чтобы...

Решение 1: используйте "рукопожатие" для доступа к HTTP

Решение 2. В вашем JavaScript на стороне клиента динамически генерируйте параметр идентификатора сеанса HTTP и отправляйте первое сообщение (через Websocket), содержащее этот идентификатор сеанса. Подключить "конечную точку" к классу cache/utility, поддерживающему идентификатор сеанса → сопоставление сеанса; избегайте утечек памяти, вы можете использовать Session Listener, например, для удаления сеанса из кеша.

P.S. Я ценю ответы от Мартина Андерссона и Йоакима Эрдателта. К сожалению, решение Martin не является потокобезопасным...