Closed scorsi closed 6 years ago
You can authenticate at the HTTP level, before the connection is upgraded.
If you are using the Jetty WebSocket API (not javax.websocket
), then create a custom WebSocketCreator
that uses the HTTP information to authenticate.
You can use the ServletUpgradeResponse
and it's sendError()
or sendForbidden()
to fail the upgrade if the authentication isn't correct.
If you have a successful authentication, then create your WebSocket endpoint and set the authenticated information you need/want on that endpoint.
Lastly, return that endpoint from your createWebSocket
implementation.
This custom WebSocketCreator
can be used from ...
WebSocketUpgradeFilter
and its various addMapping()
calls.WebSocketServletFactory
and its setCreator()
method (which you would use instead of the register()
method.@scorsi I would first look into other protocols on top of WebSocket that already do what you're probably trying to do from scratch.
For example, CometD, another project we maintain, uses the Bayeux protocol on top of either HTTP or WebSocket and provides you with a messaging API on top of WebSocket.
CometD already does some form of authentication like you describe, and what gets authenticated is the TCP connection or - equivalently - the WebSocket session on top of that TCP connection. CometD stores this information in the listener and leaves the lower level mapping (from TCP connection to WebSocket session) to Jetty. Alternatively, you can map the userId to the WebSocket session.
You authenticate the WebSocket session on the first message. Any subsequent message is implicitly authenticated by the fact that the connection has not been closed.
I will precise my project a little bit more. I'm developping a browser/mobile strategy massively multiplayer online game (to quote a known: Clash of Clans). The server (which is heavy means it carries the major part of the game logic and assets) uses Jetty with Kotlin handling WebSockets (for browsers) and later TCP and/or UDP sockets (for mobile). The goal is to have one generic protocol for all sockets types for all platforms. And so to share code logic between WebSockets and TCP/UDP Sockets.
@joakime Thank you for your message. I don't want to use the HTTP layer (as noticed above, I want to share the business logic between WebSockets and TCP/UDP Sockets). However I will look at this to mastery Jetty a little bit more :)
@sbordet Thank you for your message. I already looked at CometD and to be honest I don't really understand what it does exactly, it seems really heavy and to do lot of things where I only need one. I would like to only use WebSockets (and later TCP/UDP sockets) layer. Does CometD handle TCP/UDP sockets ?
@scorsi CometD does not handle TCP/UDP.
What CometD does is just what you are doing: implementing a protocol on top of WebSocket (and HTTP).
You will realize soon that you don't need just one thing, but you will need authentication, you will need some sort of heartbeat to handle clients that "disappear", you will need "channels" so that clients can publish messages to a channel and receive messages only when subscribed to that channel, you will need a way to batch messages, you will need a way to disconnect (from the client and from the server), you will need a way to secure the whole thing so that players cannot cheat by reverse engineering the protocol, you will need a way to guarantee the delivery and possibly the order of messages, etc. That is what CometD does.
Good luck!
We recommended that you use the HTTP layer.
The behavior difference between WebSocket and TCP/UDP is going to be rather drastic. (For WebSocket you have Endpoint objects on Client and Server, event driven. For TCP/UDP you'll have raw sockets that you have to read/write to) You'll probably wind up like most people that go down this path and end up implementing a Framing layer for their TCP/UDP connection. Suddenly you'll realize that you just implemented the bulk of websocket on the TCP/UDP layer, but without the benefits of the HTTP layer (like getting through proxies). You might want to ask if using TCP/UDP has any benefit left (both from an access to server point of view, and a code maintenance point of view)
If you want to authenticate after upgrade, that's your choice.
Now you are in the WebSocket protocol layer (RFC6455)
Unfortunately, there's no close status code for authentication issues. https://tools.ietf.org/html/rfc6455#section-7.4 You'll probably want to use a status code in the 4000-4999 range, but those are poorly supported in web browser's javascript websocket implementations.
This probably means you'll want to do the authentication at the application layer.
Now you are in the realm of application messaging, and are doing everything at the application level.
For websocket, this probably means that the client sends its authentication in it's own onOpen
or onConnect
.
The server endpoint will get a message in its onText
or onBinary
style event handlers.
You'll have to reject anything that isn't an authentication request message.
Working at the application layer, you'll have barely anything to work with.
Note: a custom WebSocketCreator
can be used to add to your created server WebSocket Endpoint details from the HTTP connection (such as remote address, uri, cookies, HttpSession, etc...).
Using the application layer, you'll have (on the server side) an anonymous connection until the authentication is performed. (don't forget to add authentication timeouts to your code).
Once the authentication is verified, you'll have to track that on your server side websocket endpoint.
@sbordet Ok, you point a good point. I may need a channel handling and push/sub (the "disapear" is the most important thing I guess) in the future, I wanted to code it by my own (also to learn). But can I easily implement CornetD client in Swift for example ?
@joakime Yeah so handling authentication in the WebSocket is "allowed" by the RFC but not really planned to. This annoys me... Yeah you're right. I should do it in the HTTP layer and upgrade to WebSocket if the bearer header/token is correct.
Thank you guys for your advices. I stopped to plan UDP/TCP socket handling. I'll so dive in CornetD and manage authentication in the HTTP layer.
@sbordet Do you have tutorial with embedded Jetty instead of the documentation ? I must admit that I don't understand where to start and where to find the info I need. I looked at 3.4.2 and 9.3 but I donr't understand how to fit the both parts. And the demo didn't use embedded Jetty. Is the documentation for version 4 ready or does the version 3 compatible with the 4 ?
@scorsi I would not discard entirely your approach. It's a lot of work, but it may be that CometD does not fit your case. I think you need to spend more time gathering information about possible solutions before starting to write your own from scratch.
@joakime has good points about proxies, and we have experience about internet at large where WebSocket does not work that well (proxies, antiviruses, etc.), while HTTP and HTTP/2 seem to be more reliable.
The documentation for CometD is at https://docs.cometd.org/current/reference/.
CometD with embedded Jetty is described at https://docs.cometd.org/current/reference/#_installation_jetty_embedded. The code in the documentation is actually taken from the code examples.
CometD 4 will be mostly compatible with 3, with only few small differences.
CometD 3 is based on JDK 7 and Jetty 9.2.x, but works with Jetty 9.4.x. CometD 4 is based on JDK 8 and Jetty 9.4.x.
If you have more questions on CometD, I recommend you switch to that project (you can open an issue there).
Thanks!
Link to cometd organization on github - https://github.com/cometd/
Hi,
I'm creating a websocket-only server using
jetty-websocket-servlet
. I've successfuly written a echo server.I'm writting a protocol over websocket for my application, the first step is to authenticate the user, to do so, the client sends its connection information over the websocket and the server authorize it using a H2 database or close the connection if information are invalide or if the first message is not a AUTH one. But now, I'm asking how to keep in the websocket session the user id after authentication.
Should I use information in the
session.remote.inetSocketAddress
and retrieve the user information thanks to a in-memory cache system ? Should I create auserId
directly in my class extendingorg.eclipse.jetty.websocket.api.WebSocketAdapter/WebSocketListener
? Should I usejavax.websocket
with Jetty (as in https://github.com/jetty-project/embedded-jetty-websocket-examples/tree/master/javax.websocket-example) to have asessionId
directly on my session object ?What of thoses solutions are the more fast, reliable and secure ?
Thank you, scorsi,