Zugreifen auf HttpSession von HttpServletRequest in einem Web Socket @ServerEndpoint
Ist es möglich, den HttpServletRequest in einer @ServerEndpoint? Vor allem bin ich versucht, es zu erhalten, so kann ich den Zugriff auf die HttpSession-Objekt.
InformationsquelleAutor der Frage Archimedes Trajano | 2013-07-30
Du musst angemeldet sein, um einen Kommentar abzugeben.
Nun zurück zu der ursprünglichen Antwort von August 2013...
Die Antwort von Martin Andersson hat ein concurrency-Fehler. Der Konfigurator aufgerufen werden können, indem mehrere threads zur gleichen Zeit, ist es wahrscheinlich, dass Sie nicht haben Zugriff auf die richtige HttpSession-Objekt zwischen die Anrufe von
modifyHandshake()
undgetEndpointInstance()
.Oder mit anderen Worten...
Hier eine änderung zu Martins code, der verwendet
ServerEndpointConfig.getUserProperties()
Karte, um dieHttpSession
Verfügung, um Ihre socket-Instanz, die während der@OnOpen
Aufruf der MethodeGetHttpSessionConfigurator.java
GetHttpSessionSocket.java
Bonus-Funktion: keine
instanceof
oder Gießen erforderlich.Einige EndpointConfig Wissen
EndpointConfig
Objekte existieren pro "Endpunkt-Instanz".Jedoch eine "Endpunkt-Instanz" hat 2 Bedeutungen mit der Skillung.
javax.websocket.Session
Bande zusammen, die endpoint-Objekt-Instanz, mit deren Konfiguration, die auf eine bestimmte logische Verbindung.Ist es möglich, eine singleton-Endpunkt-Instanz verwendet wird, der für mehrere
javax.websocket.Session
Instanzen (das ist eines der features, dieServerEndpointConfig.Configurator
unterstützt)Den ServerContainer Umsetzung verfolgen eine Reihe von ServerEndpointConfig ist, repräsentieren alle der bereitgestellten Endpunkte, die der server reagieren kann, um eine websocket-upgrade-Anfrage.
Diese ServerEndpointConfig Objekt-Instanzen können aus ein paar verschiedenen Quellen.
javax.websocket.server.ServerContainer.addEndpoint(ServerEndpointConfig)
javax.servlet.ServletContextInitializer.contextInitialized(ServletContextEvent sce)
nennenjavax.websocket.server.ServerApplicationConfig.getEndpointConfigs(Set)
nennen.@ServerEndpoint
annotierten Klassen.Diese
ServerEndpointConfig
Objekt-Instanzen vorhanden sind, als Standardwerte, wenn einjavax.websocket.Session
schließlich erstellt werden.ServerEndpointConfig.Konfigurator-Instanz
Vor jedem upgrade Anfragen eingegangen sind oder verarbeitet, alle
ServerEndpointConfig.Configurator
Objekte existieren nun und sind bereit, Sie zu führen Ihre Haupt-und einzige Zweck, um Anpassung zu ermöglichen, den upgrade-Prozess von einer websocket-Verbindung um eine eventuellejavax.websocket.Session
Zugriff auf Session-spezifische EndpointConfig
Beachten Sie, dass Sie keinen Zugriff auf die
ServerEndpointConfig
Objekt-Instanzen innerhalb einer Endpunkt-Instanz. Sie können nur aufEndpointConfig
Instanzen.Dies bedeutet, wenn Sie
ServerContainer.addEndpoint(new MyCustomServerEndpointConfig())
während der Bereitstellung und später den Zugriff versucht es über die Anmerkungen, es wird nicht funktionieren.Alle der folgenden wäre ungültig.
Können Sie den Zugriff auf die EndpointConfig während der Lebensdauer des Endpoint-Objekt-Instanz, aber unter eine begrenzte Zeit. Die
javax.websocket.Endpoint.onOpen(Session,Endpoint)
kommentierte@OnOpen
Methoden, oder über die Verwendung von CDI. Die EndpointConfig ist nicht verfügbar in sonstiger Weise oder zu jeder anderen Zeit.Können Sie jedoch immer auf das UserProperties-über die
Session.getUserProperties()
anrufen, die ist immer verfügbar. Diese Benutzer-Eigenschaften, die Karte ist immer verfügbar, sei es über die kommentierte Techniken (z.B. als Session-parameter während@OnOpen
@OnClose
@OnError
oder@OnMessage
Anrufe), die über die CDI-Einspritzung der Sitzung, oder auch mit der Verwendung von nicht-annotierten websockets, die sich vonjavax.websocket.Endpoint
.Wie Upgrade Funktioniert
Wie gesagt, jeder der definierten Endpunkte eine
ServerEndpointConfig
zugeordnet.Diese
ServerEndpointConfigs
sind eine einzige Instanz darstellt, ist der standardmäßige Zustand desEndpointConfig
die schließlich zur Verfügung gestellt, um die Endpoint-Instanzen, die möglicherweise und schließlich erstellt.Wenn ein eingehender Antrag auf ein upgrade kommt, hat es gehen Sie durch die folgenden auf der JSR.
Zu beachten, die ServerEndpointConfig.Konfigurator ist ein singleton, pro zugeordnet ServerContainer Endpunkt.
Dies ist beabsichtigt und gewünscht, zu ermöglichen implementors mehrere Funktionen.
Wenn die Implementierungen erstellt einen neuen Konfigurator für jeden handshake, diese Technik nicht möglich wäre.
(Offenlegung: ich Schreibe und Pflege der JSR-356-Implementierung für Jetty 9)
InformationsquelleAutor der Antwort Joakim Erdfelt
Vorwort
Es ist unklar, ob Sie wollen, sind die
HttpServletRequest
dieHttpSession
oder-Eigenschaften aus derHttpSession
. Meine Antwort wird zeigen, wie man dieHttpSession
oder individuelle Eigenschaften.Habe ich weggelassen null und index bounds-checks für die Kürze.
Sicherheitshinweise
Ist es schwierig. Die Antwort von Martin Andersson ist nicht korrekt, weil die gleiche Instanz von
ServerEndpointConfig.Configurator
verwendet wird, für jede Verbindung, daher existiert eine race condition. Während die ärzte sagen, dass Die "Umsetzung erstellt eine neue Instanz der Konfigurator pro logischen Endpunkt," die Skillung nicht klar zu definieren eine "logische Endpunkt." Basierend auf dem Kontext des ganzen Orte, die phrase ist verwendet, es scheint zu bedeuten, die Bindung einer Klasse, Konfigurator, den Pfad und sonstige Optionen, D. H., eineServerEndpointConfig
das ist klar verteilt. Trotzdem können Sie leicht sehen, ob eine Implementierung mithilfe der gleichen Instanz von drucken aus seinertoString()
innerhalbmodifyHandshake(...)
.Mehr überraschend, die Antwort von Joakim Erdfelt auch nicht zuverlässig funktioniert. Der text des JSR 356 selbst nicht erwähnt
EndpointConfig.getUserProperties()
ist es nur in der JavaDoc, und nirgends scheint es, angegeben werden, was seine genaue Beziehung zuSession.getUserProperties()
. In der Praxis werden einige Implementierungen (z.B. Glassfish) wieder den gleichenMap
Instanz für alle AufrufeServerEndpointConfig.getUserProperties()
während andere (z.B. Tomcat 8) nicht. Sie können überprüfen, indem Sie drucken sich die Karte Inhalte, bevor Sie es ändern innerhalbmodifyHandshake(...)
.Um zu überprüfen, kopierte ich den code direkt aus den anderen Antworten und dann wird es getestet, gegen ein Multithread-client, den ich schrieb. In beiden Fällen, die ich beobachtet habe die falsche Sitzung im Zusammenhang mit der Endpunkt-Instanz.
Skizzieren von Lösungen
Habe ich zwei Lösungen entwickelt, die habe ich schon überprüft, funktionieren ordnungsgemäß, wenn Sie getestet gegen ein Multithread-client. Es gibt zwei wesentliche tricks.
Erste, verwenden Sie einen filter mit dem gleichen Pfad wie die WebSocket. Dadurch erhalten Sie Zugriff auf die
HttpServletRequest
undHttpSession
. Es gibt Ihnen auch eine chance, um eine Sitzung zu erstellen, wenn es nicht bereits vorhanden ist (obwohl in diesem Fall die Verwendung einer HTTP-Sitzung an alle, scheint zweifelhaft).Zweitens finden Sie einige Eigenschaften, die existieren, sowohl in der WebSocket -
Session
undHttpServletRequest
oderHttpSession
. Es stellt sich heraus, es gibt zwei Kandidaten:getUserPrincipal()
undgetRequestParameterMap()
. Ich werde Ihnen zeigen, wie Sie Missbrauch sowohl von Ihnen 🙂Lösung mit User Principal
Die einfachste Art und Weise zu nutzen, ist
Session.getUserPrincipal()
undHttpServletRequest.getUserPrincipal()
. Der Nachteil ist, das könnte stören mit anderen legitimen Nutzungen dieser Eigenschaft, so verwenden Sie es nur, wenn Sie bereit sind, für diese Implikationen.Wenn Sie möchten, speichern Sie einfach eine Zeichenfolge, wie beispielsweise eine Benutzer-ID, das ist eigentlich nicht zu viel von einem Missbrauch, obwohl Sie vermutlich sollte setzen Sie in einige container managed Weise statt überschreiben Sie den wrapper, so dass ich Ihnen zeigen werde. Trotzdem würden Sie tun, indem Sie einfach überschreiben
Principal.getName()
. Dann brauchen Sie nicht einmal zu Gießen es in dieEndpoint
. Aber wenn Sie Magen können Sie auch passieren die ganzeHttpSession
Objekt wie folgt.PrincipalWithSession.java
WebSocketFilter.java
WebSocketEndpoint.java
Lösung Mittels Des Request-Parameter
Die zweite option verwendet
Session.getRequestParameterMap()
undHttpServletRequest.getParameterMap()
. Beachten Sie, dass es verwendetServerEndpointConfig.getUserProperties()
aber es ist sicher in diesem Fall, weil wir uns immer setzen, die dasselbe Objekt in der Karte anzeigen, so, ob es freigegeben ist, macht keinen Unterschied. Die einzigartige session-id übergeben wird, nicht über die user-Parameter, sondern durch die request-Parameter, die ist einmalig pro Anfrage.Diese Lösung ist etwas weniger hacky, weil es nicht stört mit dem user principal-Eigenschaft. Beachten Sie, dass wenn Sie brauchen, um passieren die tatsächlichen request-Parameter zusätzlich zu den ein, der eingefügt wurde, können Sie das leicht tun: starten Sie einfach mit der vorhandenen request-parameter-map anstatt eine neue leere wie hier gezeigt. Aber achten Sie darauf, dass der Benutzer kann nicht fälschen die speziellen parameter Hinzugefügt, die in den filter, indem Sie die Versorgung Ihrer eigenen request-parameter mit demselben Namen in der aktuellen HTTP Anfrage.
SessionTracker.java
WebSocketFilter.java
WebSocketEndpoint.java
Lösung für Einzelne Eigenschaften
Wenn Sie nur bestimmte Eigenschaften aus der
HttpSession
und nicht die ganzeHttpSession
selbst, wie sagen, eine Benutzer-ID, dann könnten Sie tun, Weg mit der ganzenSessionTracker
business und nur die erforderlichen Parameter in die Karte, die Sie nach Ihrer Außerkraftsetzung desHttpServletRequestWrapper.getParameterMap()
. Dann können Sie auch loszuwerden, die GewohnheitConfigurator
; Ihre Eigenschaften werden bequem vonSession.getRequestParameterMap()
im Endpunkt.WebSocketFilter.java
WebSocketEndpoint.java
InformationsquelleAutor der Antwort Chad N B
Ist es möglich?
Lasst uns die Java API für WebSocket Spezifikation, um zu sehen, ob zu ergattern, die
HttpSession
Objekt ist möglich. Die Spezifikation sagt auf Seite 29:Also ja, es ist möglich.
Aber ich glaube nicht, dass es für Sie möglich ist, ein Verweis auf die
HttpServletRequest
Objekt wenn. Man konnte hören für alle neues servlet, das Anfragen über einServletRequestListener
aber Sie würde noch herausfinden, welcher Anforderung gehören, die server-Endpunkt. Bitte lassen Sie mich wissen, wenn Sie eine Lösung finden!Abstract how-to
How-to lose ist beschrieben auf den Seiten 13 und 14 in der Spezifikation und am Beispiel von mir in den code, der unter der nächsten überschrift.
In Englisch, wir müssen Sie abfangen der handshake-Prozess, ein
HttpSession
Objekt. Dann übertragen Sie die HttpSession Verweis auf unsere server-Endpunkt, wir müssen auch abfangen, wenn der container erstellt der server-Endpunkt-Instanz und manuell injizieren der Referenz. All das tun wir, indem wir unseren eigenenServerEndpointConfig.Configurator
und überschreiben Sie die MethodenmodifyHandshake()
undgetEndpointInstance()
.Den custom-Konfigurator wird einmal instanziiert pro logischen
ServerEndpoint
(Siehe JavaDoc).Code-Beispiel
Dies ist der server, endpoint-Klasse (ich Stelle die Umsetzung der CustomConfigurator Klasse nach diesem code-snippet):
- Und dies ist die benutzerdefinierte Konfigurator:
InformationsquelleAutor der Antwort Martin Andersson
Alle oben genannten Antworten zu Lesen lohnt, aber keiner von Ihnen löst OP ' s (und mein) problem.
Können Sie den Zugriff HttpSession, wenn ein WS Endpunkt ist die Eröffnung und übergabe an neu geschaffenen end-point-Instanz, aber niemand garantiert es existiert HttpSession Instanz!
Also müssen wir Schritt 0, bevor diese hacken (ich hasse JSR 365 Implementierung des WebSocket -).
Websocket - httpSession wird null zurückgegeben
InformationsquelleAutor der Antwort 9dan
Alle möglichen Lösungen basieren auf:
A. Client-browser-Implementierungen verwaltet Session-ID via Cookie übergebene Wert als HTTP-Header, oder (wenn cookies deaktiviert sind) ist es gelungen, durch Servlet-container generiert die Session-ID postfix für die generierten URLs
B. können Sie den Zugriff HTTP-Request-Header nur bei HTTP-handshake; danach wird das Websocket-Protokoll
So, dass...
Lösung 1: verwenden Sie das "handshake", um Zugriff auf HTTP
Lösung 2: Im JavaScript auf der client-Seite dynamisch generieren HTTP-Sitzungs-ID-parameter und senden der ersten Nachricht (über Websocket), die mit dieser Session-ID. Verbinden Sie "Endpunkt", um die cache - /utility-Klasse Aufrechterhaltung der Session-ID -> Session-mapping; vermeiden von Speicherlecks, die Sie verwenden können, Session-Listener-Instanz zu entfernen, die Sitzung aus dem cache.
P. S. ich Schätze, die Antworten von Martin Andersson und Joakim Erdfelt.
Leider, Lösung von Martin ist nicht thread-sicher...
InformationsquelleAutor der Antwort Fuad Efendi
Der einzige Weg, der funktioniert in allen Anwendungen, Servern verwenden ThreadLocal. Siehe:
https://java.net/jira/browse/WEBSOCKET_SPEC-235
InformationsquelleAutor der Antwort Ryan