Accéder à HttpSession à partir de HttpServletRequest dans une Socket Web @ServerEndpoint

est-il possible d'obtenir la requête Httpservleter à l'intérieur d'un @ServerEndpoint? J'essaie avant tout de l'obtenir pour accéder à L'objet HttpSession.

43
demandé sur Andrei I 2013-07-30 04:26:26

6 réponses

mise à jour (novembre 2016) : les informations fournies dans cette réponse sont pour la spécification JSR356, les implémentations individuelles de la spécification peuvent varier en dehors de ces informations. D'autres suggestions trouvées dans les commentaires et les autres réponses sont toutes des comportements spécifiques à la mise en œuvre en dehors de la norme JSR356.

si les suggestions ici vous causent des problèmes, mettez à jour vos diverses installations de Jetty, Tomcat, Wildfly, ou Glassfish / Tyrus. Toutes les versions actuelles de ces implémentations ont été rapportées à travailler de la manière décrite ci-dessous.

maintenant retour à la réponse originale de août 2013 ...

la réponse de Martin Andersson présente un défaut de concordance. Le Configurateur peut être appelée par plusieurs threads en même temps, il est probable que vous n'aurez pas accès à la bonne Objet HttpSession entre les appels de modifyHandshake() et getEndpointInstance() .

ou dit autrement...

  • Demande Un
  • Modifier Poignée De Main A
  • Demande B
  • Modifier Poignée De Main B
  • Obtenir le point de Terminaison Exemple Un <-- ceci aurait Demande B HttpSession
  • Obtenir De L'Extrémité De L'Instance B

Voici une modification au code de Martin qui utilise ServerEndpointConfig.getUserProperties() map pour rendre le HttpSession disponible à votre instance de socket pendant la @OnOpen appel de méthode

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

Bonus: pas de instanceof ou de coulée nécessaire.

Certains EndpointConfig Connaissances

EndpointConfig objets n'existent pas par "point de Terminaison de l'Instance".

Toutefois, un "point de Terminaison de l'Instance" a 2 significations avec la spécification.

  1. comportement par défaut du JSR, où chaque requête de mise à niveau entrante produit une nouvelle instance objet de la classe endpoint
  2. A javax.websocket.Session qui relie l'instance de fin d'objet, avec son configuration à une connexion logique.

il est possible d'avoir une instance de paramètre unique utilisée pour plusieurs javax.websocket.Session instances (c'est l'une des caractéristiques que ServerEndpointConfig.Configurator supporte)

L'implémentation ServerContainer va suivre un ensemble de ServerEndpointConfig qui représentent tous les endpoints déployés que le serveur peut répondre à une demande de mise à niveau de websocket.

ces Les instances d'objet ServerEndpointConfig peuvent provenir de quelques sources différentes.

  1. manuellement fourni par le javax.websocket.server.ServerContainer.addEndpoint(ServerEndpointConfig)
    • habituellement fait dans un javax.servlet.ServletContextInitializer.contextInitialized(ServletContextEvent sce) appel
  2. de l'appel javax.websocket.server.ServerApplicationConfig.getEndpointConfigs(Set) .
  3. créé automatiquement à partir de la numérisation de l'application web pour les classes annotées @ServerEndpoint .

ces ServerEndpointConfig les instances d'objet existent par défaut pour quand un javax.websocket.Session finit par être créé.

ServerEndpointConfig.Configurateur Instance

avant toute demande de mise à niveau reçue ou traitée, tous les objets ServerEndpointConfig.Configurator existent maintenant et sont prêts à accomplir leur but principal et unique, pour permettre la personnalisation du processus de mise à niveau d'une connexion websocket à un éventuel javax.websocket.Session

l'Accès à la Session spécifique EndpointConfig

notez que vous ne pouvez pas accéder aux instances d'objet ServerEndpointConfig à partir d'une instance endpoint. Vous ne pouvez accéder qu'aux instances EndpointConfig .

cela signifie que si vous avez fourni ServerContainer.addEndpoint(new MyCustomServerEndpointConfig()) pendant le déploiement et essayé plus tard d'y accéder via les annotations, cela ne fonctionnera pas.

Tout ce qui suit serait invalide.

@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
     */
}

vous pouvez accéder à EndpointConfig pendant la durée de vie de l'instance Endpoint object, mais en un temps limité. La méthode javax.websocket.Endpoint.onOpen(Session,Endpoint) , annotée @OnOpen , ou via L'utilisation du CDI. EndpointConfig n'est pas disponible d'une autre manière ou à tout autre moment.

cependant, vous pouvez toujours accéder à L'UserProperties via l'appel Session.getUserProperties() , qui est disponible toujours. Cette carte des propriétés de L'utilisateur est toujours disponible, que ce soit via les techniques annotées (telles qu'un paramètre de Session @OnOpen , @OnClose , @OnError , ou @OnMessage ), via L'injection CDI de la Session, ou même avec l'utilisation de websockets non annotés qui s'étendent à partir de javax.websocket.Endpoint .

Comment Des Travaux De Rénovation

comme nous l'avons déjà mentionné, chaque paramètre défini sera associé à un ServerEndpointConfig .

ceux ServerEndpointConfigs est une instance unique qui représente l'état par défaut du EndpointConfig qui sont éventuellement rendus disponibles pour les Instances de fin qui sont éventuellement créées.

Lorsqu'une demande de mise à niveau arrive, elle doit passer par les étapes suivantes du JSR.

  1. le chemin d'accès correspond à aucun des ServerEndpointConfig.entrées getPath()
    • Si pas de match, retour 404 de mettre à niveau
  2. pass demande de mise à niveau en ServerEndpointConfig.Configurator.checkOrigin()
    • Si pas valide, de retour d'erreur à la mise à niveau de réponse
    • créer HandshakeResponse
  3. pass demande de mise à niveau en ServerEndpointConfig.Configurator.getNegotiatedSubprotocol()
    • magasin de réponse dans HandshakeResponse
  4. pass demande de mise à niveau en ServerEndpointConfig.Configurator.getNegotiatedExtensions()
    • magasin de réponse dans HandshakeResponse
  5. créer un nouvel objet ServerEndpointConfig spécifique à un terminal. copiez les encodeurs, les décodeurs et les propriétés des utilisateurs. Ce nouveau ServerEndpointConfig contient les paramètres par défaut pour path, extensions, endpoint class, subprotocols, configurator.
  6. pass demande de mise à niveau, d'intervention et de nouveau ServerEndpointConfig dans ServerEndpointConfig.Configurator.modifyHandshake ()
  7. call ServerEndpointConfig.getEndpointClass ()
  8. utilisation de la classe sur ServerEndpointConfig.Configurator.getEndpointInstance (Class)
  9. crée Session, associe l'instance endpoint et L'objet EndpointConfig.
  10. Informer extrémité exemple de se connecter
  11. méthodes annotées qui veulent EndpointConfig obtient celui associé à cette Session.
  12. appelle à la Session.getUserProperties () retourne EndpointConfig.getUserProperties ()

à noter, Le ServerEndpointConfig.Configurator est un paramètre unique, par serveur cartographié.

c'est intentionnel, et souhaité, pour permettre aux implémenteurs plusieurs fonctionnalités.

  • pour retourner le même paramètre d'instance pour plusieurs pairs s'ils donc le désir. L'approche "apatride" de l'écriture websocket.
  • pour avoir un point unique de gestion de ressources coûteuses pour tous les postes de travail les instances

si les implémentations créaient un nouveau configurateur pour chaque poignée de main, cette technique ne serait pas possible.

(Divulgation: je écrire et à maintenir la JSR 356 de la mise en œuvre de la Jetée 9)

70
répondu Joakim Erdfelt 2016-11-28 14:21:02

Préface

il n'est pas clair si vous voulez le HttpServletRequest , le HttpSession , ou des propriétés hors du HttpSession . Ma réponse montrera comment obtenir le HttpSession ou des propriétés individuelles.

j'ai omis les vérifications null et Index pour la brièveté.

précautions

c'est délicat. La réponse de Martin Andersson n'est pas correcte parce que la même instance de ServerEndpointConfig.Configurator est utilisé pour chaque connexion, donc une condition de race existe. Alors que le docs déclare que "L'implémentation crée une nouvelle instance du configurateur par point final logique", le spec ne définit pas clairement un "point final logique"."Basé sur le contexte de tous les endroits où cette phrase est utilisée, il semble signifier la liaison d'une classe, configurateur, chemin, et d'autres options, i.e., un ServerEndpointConfig , qui est clairement partagé. De toute façon, vous pouvez facilement voir si une implémentation utilise la même instance par imprimer son toString() de l'intérieur de modifyHandshake(...) .

plus étonnamment, la réponse de Joakim Erdfelt ne fonctionne pas non plus de manière fiable. Le texte de JSR 356 lui-même ne mentionne pas EndpointConfig.getUserProperties() , il est seulement dans le JavaDoc, et nulle part ne semble-t-il être précisé ce que son rapport exact est à Session.getUserProperties() . En pratique, certaines implémentations (par exemple Glassfish) renvoient la même instance Map pour tous les appels à ServerEndpointConfig.getUserProperties() tandis que d'autres (par exemple Tomcat 8) ne le fais pas. Vous pouvez vérifier en imprimant le contenu de la carte avant de la modifier dans modifyHandshake(...) .

pour vérifier, j'ai copié le code directement à partir des autres réponses et je l'ai testé contre un client multithread que j'ai écrit. Dans les deux cas, j'ai observé que la mauvaise session était associée à l'instance du paramètre.

esquisse de Solutions

j'ai développé deux solutions, que j'ai vérifié le travail correctement lors de tests contre un client multithread. Il y a deux astuces.

tout d'abord, utilisez un filtre avec le même chemin que le WebSocket. Vous aurez ainsi accès au HttpServletRequest et au HttpSession . Il vous donne également la possibilité de créer une session si elle n'existe pas déjà (bien que dans ce cas l'utilisation D'une session HTTP semble douteuse).

Deuxièmement, trouver certaines propriétés qui existent à la fois dans le WebSocket Session et HttpServletRequest ou HttpSession . Il il s'avère qu'il y a deux candidats: getUserPrincipal() et getRequestParameterMap() . Je vais vous montrer comment l'abus de tous les deux :)

Solution à l'aide de l'Utilisateur Principal

la façon la plus facile est de profiter de Session.getUserPrincipal() et HttpServletRequest.getUserPrincipal() . L'inconvénient est que cela pourrait gêner d'autres utilisations légitimes de ce bien, afin de l'utiliser seulement si vous êtes prêt pour ces incidences.

si vous voulez stocker une seule chaîne, comme une nom d'utilisateur, ce n'est en fait pas trop un abus, bien qu'il devrait probablement être mis dans un conteneur géré de manière plutôt que d'écraser l'emballage comme je vais vous le montrer. De toute façon, vous feriez cela en remplaçant simplement Principal.getName() . Alors vous n'avez même pas besoin de le lancer dans le Endpoint . Mais si vous pouvez l'encaisser, vous pouvez également passer l'objet entier HttpSession comme suit.

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

Solution En Utilisant Les Paramètres De La Demande

la deuxième option utilise Session.getRequestParameterMap() et HttpServletRequest.getParameterMap() . Notez qu'il utilise ServerEndpointConfig.getUserProperties() mais il est sûr dans ce cas car nous mettons toujours le même objet dans la carte, donc si elle est partagée ne fait aucune différence. L'identifiant unique de la session n'est pas passé par les paramètres de l'utilisateur, mais par le paramètres de la requête, qui est unique par requête.

cette solution est un peu moins hacky parce qu'elle n'interfère pas avec la propriété principale de l'utilisateur. Notez que si vous avez besoin de passer par les paramètres de requête actual en plus de celui qui est inséré, Vous pouvez facilement le faire: il suffit de commencer avec la table des paramètres de requête existante au lieu d'une nouvelle table vide comme indiqué ici. Mais prenez garde à ce que l'utilisateur ne peut pas usurper le paramètre spécial ajouté dans le filtre en fournissant leur propre paramètre request par le même nom dans la requête HTTP actuelle.

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's the same instance of SessionTracker all the time
            sec.getUserProperties().put(SessionTracker.class.getName(), tracker);
            super.modifyHandshake(sec, request, response);
        }
    }
}

Solution pour propriétés simples

si vous n'avez besoin que de certaines propriétés du HttpSession et non de l'ensemble HttpSession lui-même, comme un nom d'utilisateur, alors vous pourriez supprimer l'ensemble SessionTracker de l'entreprise et juste mettre les paramètres nécessaires dans la carte que vous retournez à partir de votre outrepasser de HttpServletRequestWrapper.getParameterMap() . Ensuite, vous pouvez également vous débarrasser de la coutume Configurator ; vos propriétés seront commodément accessibles à partir de Session.getRequestParameterMap() dans le point final.

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);
    }
}
16
répondu Chad N B 2015-05-12 00:58:42

est-ce possible?

examinons la spécification de L'API Java pour WebSocket pour voir si l'obtention de l'objet HttpSession est possible. La spécification dit à la page 29:

parce que les connexions websocket sont initialisées par une requête http, il y a une association entre la HttpSession sous laquelle un client fonctionne et tous les websockets qui sont établi dans ce HttpSession. L'API permet l'accès dans la poignée de main d'ouverture à la HttpSession unique correspondant à ce même client.

Donc oui c'est possible.

cependant, je ne pense pas qu'il soit possible pour vous de mettre la main sur une référence à l'objet HttpServletRequest cependant. Vous pouvez écouter tout nouvelles requêtes servlet en utilisant un ServletRequestListener , mais tu le ferais quand même. quelle demande appartenir à quel point d'arrêt du serveur. Faites-moi savoir si vous trouvez une solution!

Résumé sur la façon 1519110920"

est vaguement décrits aux pages 13 et 14 du cahier des charges et illustré par moi-même dans le code de la prochaine rubrique.

En anglais, nous aurons besoin d'intercepter le processus de négociation d'un HttpSession objet. Pour ensuite transférer la référence HttpSession sur notre serveur endpoint, nous devons également intercepter lorsque le conteneur crée l'instance endpoint du serveur et injecter manuellement la référence. Nous faisons tout cela en fournissant notre propre ServerEndpointConfig.Configurator et en outrepassant les méthodes modifyHandshake() et getEndpointInstance() .

le configurateur personnalisé sera instancié une fois par logical ServerEndpoint (voir le JavaDoc ).

exemple de Code

c'est la classe endpoint du serveur (I fournir l'implémentation de la classe CustomConfigurator après cet extrait de code):

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

Et voici le configurateur personnalisé:

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;
    }
}
7
répondu Martin Andersson 2016-06-09 12:42:14

toutes les réponses ci-dessus valent la peine d'être lues, mais aucune ne résout le problème des OP (et de moi).

vous pouvez accéder à HttpSession quand un point final WS s'ouvre et le passe à une instance de point final nouvellement créée, mais personne ne garantit qu'il existe une instance HttpSession!

nous avons donc besoin de l'étape 0 avant ce piratage (je déteste la mise en œuvre JSR 365 de WebSocket). Websocket - httpSession retourne null

4
répondu 9dan 2017-05-23 10:31:28

toutes les solutions possibles sont basées sur:

A. les implémentations du navigateur Client maintiennent L'ID de Session via la valeur du Cookie passée comme un en-tête HTTP, ou (si les cookies sont désactivés) elle est gérée par le conteneur Servlet qui générera postfix de L'ID de Session pour les URLs générées

B. Vous pouvez accéder aux en-têtes de requête HTTP uniquement pendant la poignée de main HTTP; Après cela, C'est le protocole Websocket

donc ça...

Solution 1: utilisez "handshake" pour accéder à HTTP

Solution 2: dans votre JavaScript côté client, générez dynamiquement le paramètre HTTP Session ID et envoyez le premier message (via Websocket) contenant ce paramètre. Connectez "endpoint" à la classe cache / utility qui maintient L'ID de Session -> cartographie de Session; évitez les fuites de mémoire, vous pouvez utiliser Session Listener par exemple pour supprimer la session du cache.

P.S. J'apprécie les réponses de Martin Andersson et Joakim Erdfelt. Malheureusement, la solution de Martin n'est pas sans fil...

0
répondu Fuad Efendi 2014-09-17 17:24:41

la seule façon qui fonctionne sur tous les serveurs d'applications est D'utiliser ThreadLocal. Voir:

https://java.net/jira/browse/WEBSOCKET_SPEC-235

0
répondu Ryan 2017-02-10 16:51:59