L'accès HttpSession de HttpServletRequest dans un site Web Socket @ServerEndpoint
Est-il possible d'obtenir le HttpServletRequest à l'intérieur d'un @ServerEndpoint? Surtout que j'essaie de le faire afin que je puisse accéder à l'objet HttpSession.
Vous devez vous connecter pour publier un commentaire.
Maintenant de retour à l'original de la réplique de août 2013...
La réponse de Martin Andersson a un défaut de simultanéité. Le Configurateur peut être appelée par plusieurs threads en même temps, il est probable que vous n'aurez pas accès à la correcte objet HttpSession entre les appels de
modifyHandshake()
etgetEndpointInstance()
.Ou dit d'une autre manière...
Voici une modification de Martin de code qui utilise des
ServerEndpointConfig.getUserProperties()
carte pour faire de laHttpSession
à la disposition de votre prise de l'instance au cours de la@OnOpen
appel de la méthodeGetHttpSessionConfigurator.java
GetHttpSessionSocket.java
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".Cependant, un "point de Terminaison de l'Instance" a 2 significations avec la spécification.
javax.websocket.Session
qui relie l'objet d'extrémité exemple, avec sa configuration, à une logique spécifique de connexion.Il est possible d'avoir un singleton Extrémité exemple être utilisé pour de multiples
javax.websocket.Session
cas (qui est une des caractéristiques quiServerEndpointConfig.Configurator
prend en charge)La ServerContainer mise en œuvre permettra de suivre un ensemble de ServerEndpointConfig qui représentent l'ensemble de l'déployé des points de terminaison que le serveur peut répondre à un websocket demande de mise à niveau.
Ces ServerEndpointConfig des instances de l'objet peut provenir de différentes sources.
javax.websocket.server.ServerContainer.addEndpoint(ServerEndpointConfig)
javax.servlet.ServletContextInitializer.contextInitialized(ServletContextEvent sce)
appeljavax.websocket.server.ServerApplicationConfig.getEndpointConfigs(Set)
appel.@ServerEndpoint
annoté classes.Ces
ServerEndpointConfig
des instances de l'objet exister en tant que valeurs par défaut lorsqu'unejavax.websocket.Session
n'a finalement créé.ServerEndpointConfig.Configurateur Instance
Avant toute mise à niveau des demandes sont reçues ou traitées, toutes les
ServerEndpointConfig.Configurator
objets existent et sont prêts à effectuer leur principal et unique objectif de permettre à des fins de personnalisation du processus de mise à niveau d'une connexion websocket pour une éventuellejavax.websocket.Session
Accès à la Session spécifique EndpointConfig
Remarque, vous ne pouvez pas accéder à la
ServerEndpointConfig
des instances de l'objet à partir de l'intérieur d'une extrémité de l'instance. Vous ne pouvez accéder àEndpointConfig
instances.Cela signifie que si vous avez fourni
ServerContainer.addEndpoint(new MyCustomServerEndpointConfig())
au cours de déployer et a ensuite essayé d'y accéder via les annotations, il ne fonctionnera pas.Toutes les conditions suivantes ne sont pas valables.
Vous pouvez accéder à la EndpointConfig au cours de la vie de l'Extrémité de l'objet de l'instance, mais dans un temps limité. Le
javax.websocket.Endpoint.onOpen(Session,Endpoint)
, annoté@OnOpen
méthodes, ou via l'utilisation de CDI. Le EndpointConfig n'est pas disponible de toute autre manière ou à tout autre moment.Cependant, vous pouvez toujours accéder à la UserProperties via le
Session.getUserProperties()
appel, ce qui est toujours disponible. Cet Utilisateur Propriétés de la carte est toujours disponible, que ce soit via le annoté techniques (comme un paramètre Session au cours de@OnOpen
,@OnClose
,@OnError
, ou@OnMessage
appels), via CDI injection de la Session, ou même avec l'utilisation de la non-annotés websockets qui s'étendent dejavax.websocket.Endpoint
.Comment Des Travaux De Rénovation
Comme indiqué précédemment, tous les points de terminaison définis aura un
ServerEndpointConfig
associés.Ceux
ServerEndpointConfigs
sont une seule instance qui représente l'état par défaut de laEndpointConfig
qui sont éventuellement mis à la disposition du point de Terminaison des Instances qui sont peut-être et finalement créé.Quand une arrivée de la mise à niveau de l'arrivée des demandes, il a passer par la suite sur la JSR.
À noter, l'ServerEndpointConfig.Le configurateur est un singleton, par mappé ServerContainer point de terminaison.
C'est intentionnel, et a souhaité, pour permettre à des réalisateurs de plusieurs fonctionnalités.
Si les implémentations créé 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)
EndpointConfig
objet passé en votre@OnOpen
méthode, si vous êtes à l'aide de Tyr, n'est pas votre configuration personnalisée de l'objet, mais c'est le défaut? Qui m'a fait pas confiance à l'utilisateur propriétés de la carte. En outre, vous n'avez pas à utiliser l'opérateur instanceof. Je l'ai utilisé pour nous assurer d'obtenir un bon type d'exception si un programmeur utilise la classe personnalisée de façon inappropriée. 2. Il peut ressembler à une simultanéité de défaut mais il ne l'est pas. J'ai souligné dans ma réponse déjà. La JavaDoc dit: "La mise en œuvre crée une nouvelle instance de l'outil de configuration par logique, point final".getEndpointInstance
méthode à tous.javax.websocket
, pas le "WebSocket standard".modifyHandshake
reçoit l'originalServerEndpointConfig
et pas la poignée de main de copie. Par rapport à l'écoulement que vous décrivez,modifyHandshake
(point 6) se produit avant mêmecheckOrigin
(point 2). C'est exactement la même chose avec GlassFish 4, donc je ne vois pas comment à l'aide de userProperties() est plus de threads que d'utiliser les variables d'instance de l'outil de configuration. En fait, je ne vois pas comment il est possible de transmettre quoi que ce soit entre le configurateur et le point de terminaison de l'instance actuelle de la JSR 356 impls.modifyHandshake
reçoit un "singleton'ServerEndpointConfig
de sorte qu'il n'est pas thread-safe à utiliser endpointConfig.userProperties pour stocker la session. De ce que j'ai vérifié, aucune des deux réponses à la question initiale sur l'enregistrement de la httpsession est droit.modifyHanshake
si la réponse est correcte pour Tomcat, mais pas de la Jetée et GlassFish. La spec ne semble pas à spécifier de comportement.HttpSession httpSession = (HttpSession)request.getHttpSession();
renvoie la valeur null dans mon cas. J'ai pensé à une WS connexion est lancée dans un http-request? Il devrait donc y avoir un HttpSession. Avez-vous une idée de quel est le problème ici? EDIT: il Semble bien que la HttpSession n'est pas encore créé. Je viens donc de créer une Session avant que je permettre l'accès à la WebsocketPréface
Il est difficile de savoir si vous êtes désireux de l'
HttpServletRequest
, leHttpSession
, ou les propriétés de laHttpSession
. Ma réponse va vous montrer comment obtenir leHttpSession
ou les propriétés individuelles.J'ai omis de null et limites de l'indice de contrôles pour des raisons de concision.
Précautions
C'est difficile. La réponse de Martin Andersson n'est pas correcte parce que la même instance de
ServerEndpointConfig.Configurator
est utilisé pour toutes les connexions, par conséquent, une condition de concurrence existe. Alors que les docs de l'état que "La mise en œuvre crée une nouvelle instance de l'outil de configuration par logique de point de terminaison," la spécification ne définit pas clairement une "logique d'extrémité." Basé sur le contexte de tous les endroits où cette expression est utilisée, elle signifie la liaison d'une classe, configurateur, chemin d'accès, et les autres options, c'est à dire, unServerEndpointConfig
, qui est à l'évidence partagée. De toute façon, vous pouvez facilement voir si une mise en œuvre à l'aide de la même instance par l'impression de sontoString()
de l'intérieurmodifyHandshake(...)
.Plus étonnamment, la réponse de Joakim Erdfelt aussi ne fonctionne pas de manière fiable. Le texte de la JSR 356 lui-même ne mentionne pas
EndpointConfig.getUserProperties()
, c'est seulement dans la JavaDoc, et nulle part, semble-t-il être spécifié que sa relation exacte est deSession.getUserProperties()
. Dans la pratique, certaines implémentations (par exemple, Glassfish) retournent la mêmeMap
exemple pour tous les appels àServerEndpointConfig.getUserProperties()
tandis que d'autres (par exemple, Tomcat 8) ne pas. Vous pouvez le vérifier en imprimant le contenu de la carte avant de la modifier dansmodifyHandshake(...)
.Pour vérifier, j'ai copié le code directement à partir de l'autre des réponses et puis testé à l'encontre d'un client multithread j'ai écrit. Dans les deux cas, j'ai observé la session incorrecte être associé avec le point final de l'instance.
Contour des Solutions
J'ai développé deux solutions, que j'ai vérifié fonctionner correctement lorsqu'il est testé à l'encontre d'un client multithread. Il y a deux astuces.
Tout d'abord, utiliser un filtre avec le même chemin que les WebSocket. Cela vous donnera accès à la
HttpServletRequest
etHttpSession
. Il vous donne également une chance de créer une session si elle n'existe pas déjà (bien que dans ce cas à l'aide d'une session HTTP, à tous les semble douteux).Deuxième, trouver des propriétés qui existe dans le WebSocket
Session
etHttpServletRequest
ouHttpSession
. Il s'avère, il y a deux candidats:getUserPrincipal()
etgetRequestParameterMap()
. Je vais vous montrer comment l'abus de tous les deux 🙂Solution à l'aide de l'Utilisateur Principal
Le moyen le plus facile est de prendre avantage de
Session.getUserPrincipal()
etHttpServletRequest.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 souhaitez stocker seulement une chaîne de caractères, comme un IDENTIFIANT, ce n'est effectivement pas trop d'abus, bien que sans doute il doit être réglé en quelques gérée par le conteneur façon au lieu de remplacer l'enveloppe, je vais vous montrer. De toute façon vous pouvez faire cela en seulement primordial
Principal.getName()
. Ensuite, vous n'avez même pas besoin de le jeter dans laEndpoint
. Mais si vous pouvez l'estomac, vous pouvez aussi passer l'ensembleHttpSession
objet comme suit.PrincipalWithSession.java
WebSocketFilter.java
WebSocketEndpoint.java
Solution À L'Aide De Paramètres De La Requête
La deuxième option utilise
Session.getRequestParameterMap()
etHttpServletRequest.getParameterMap()
. Notez qu'il utiliseServerEndpointConfig.getUserProperties()
mais c'est sûr, dans ce cas, parce que nous sommes toujours mettre le même objet dans la carte, de sorte que si elle est partagée ne fait aucune différence. L'identifiant de session unique n'est pas passé à travers les paramètres de l'utilisateur, mais plutôt par le biais des paramètres de la requête, qui est unique par demande.Cette solution est un peu moins hacky car il n'interfère pas avec l'utilisateur principal de la propriété. Notez que si vous devez passer par la réelle les paramètres de la demande, en plus de celle qui est insérée, vous pouvez le faire facilement: il suffit de commencer avec le paramètre de la requête de la carte au lieu d'un nouveau 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 de la requête par le même nom dans la requête HTTP.
SessionTracker.java
WebSocketFilter.java
WebSocketEndpoint.java
Solution pour les Propriétés
Si vous avez seulement besoin de certaines propriétés de la
HttpSession
et non l'ensemble de laHttpSession
lui-même, comme, disons, un ID utilisateur, puis vous pourrait faire disparaître avec l'ensemble de l'SessionTracker
d'affaires et vient de mettre les paramètres nécessaires dans la carte vous revenez de votre remplacement deHttpServletRequestWrapper.getParameterMap()
. Ensuite, vous pouvez également vous débarrasser de la coutumeConfigurator
; vos propriétés sera facilement accessible depuisSession.getRequestParameterMap()
dans le point de Terminaison.WebSocketFilter.java
WebSocketEndpoint.java
session.getUserPrincipal()
retourne toujoursnull
même si le filtre est appliqué correctement. Est-il possibile le Ressac WS n'utilise pas la servlet demande du filtre?Object tracker = ((HttpSession) request.getHttpSession())...
devrait être OK par la HandshakeRequest JavaDoc. Ce unhttpSession = ((PrincipalWithSession) webSocketSession.getUserPrincipal())...
pourrait être sketchier...cela dépend de laPrincipal
être le même que celui renvoyé parHttpServletRequest.getUserPrincipal()
...mais il n'est pas en jetant un WS session.Est-il possible?
Passons en revue les Java API WebSocket spécification pour voir si se procurer de l'
HttpSession
objet est possible. Le spécification dit à la page 29:Donc oui c'est possible.
Cependant, je ne pense pas que c'est possible pour vous d'obtenir une référence à la
HttpServletRequest
objet. Vous pouvez écouter pour tous nouvelle servlet demandes à l'aide d'unServletRequestListener
, mais vous auriez encore à comprendre ce qui demande appartenir à quel point d'arrêt du serveur. S'il vous plaît laissez-moi savoir si vous trouvez une solution!Résumé comment
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 les HttpSession référence à notre point de terminaison du serveur, nous avons également besoin d'intercepter lorsque le conteneur crée le point de terminaison du serveur d'instance et d'injecter manuellement la référence. Nous faisons tout cela en offrant à nos propresServerEndpointConfig.Configurator
et de remplacer les méthodesmodifyHandshake()
etgetEndpointInstance()
.La coutume configurateur sera instancié qu'une seule fois par logique
ServerEndpoint
(Voir la JavaDoc).Exemple de Code
C'est le serveur de point de terminaison de la classe (j'ai la mise en œuvre de la CustomConfigurator classe après cet extrait de code):
Et c'est la coutume configurateur:
ConnectionWebSocket
?Toutes les réponses ci-dessus, la peine de lire, mais aucun d'eux ne résout OP (et mon) problème.
Vous pouvez accéder à HttpSession lorsqu'un WS point de fin est l'ouverture et la passer nouvellement créé de point de fin de l'instance, mais aucune garantie n'existe pas de HttpSession exemple!
Nous avons donc besoin de l'étape 0 avant de ce piratage (je déteste JSR 365 mise en œuvre de WebSocket).
Websocket - httpSession retourne null
Tous les possibles solutions sont basées sur:
A. navigateur Client implémentations maintient l'ID de Session par l'intermédiaire de Cookies transmis sous la forme d'un en-Tête HTTP, ou (si les cookies sont désactivés) il est géré par le conteneur de Servlet qui va générer l'ID de Session postfix pour l'Url générée
B. Vous pouvez accéder à la Requête HTTP en-Têtes uniquement pendant HTTP poignée de main; après, c'est le Protocole Websocket
De sorte que...
Solution 1: utilisation de "poignée de main" pour l'accès HTTP
Solution 2: Dans votre JavaScript côté client, générer dynamiquement HTTP ID de Session paramètre et envoyer le premier message (via Websocket) contenant cet ID de Session. Se connecter "point final" à la cache /classe utilitaire conserver l'IDENTIFIANT de Session -> Session de la cartographie; éviter les fuites de mémoire, vous pouvez utiliser la Session de l'Auditeur par exemple de supprimer la session à partir du cache.
P. S. j'apprécie les réponses de Martin Andersson et Joakim Erdfelt.
Malheureusement, la solution de Martin n'est pas thread-safe...
Le seul moyen qui fonctionne sur tous les serveurs d'applications est d'utiliser ThreadLocal. Voir:
https://java.net/jira/browse/WEBSOCKET_SPEC-235