X

Pavel Bucek's Weblog

  • August 2, 2013

Securing WebSocket applications on Glassfish

Today we are going to cover deploying secured WebSocket applications on Glassfish and access to these services using WebSocket Client API.

WebSocket server application setup

Our server endpoint might look as simple as this:

@ServerEndpoint("/echo")
public class EchoEndpoint {
@OnMessage
  public String echo(String message) {
    return message + " (from your server)";
  }
}

Everything else must be configured on container level.

We
can start with enabling SSL, which will require web.xml to be added to
your project. For starters, it might look as following:

<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee">
  <security-constraint>
    <web-resource-collection>
      <web-resource-name>Protected resource</web-resource-name>
      <url-pattern>/*</url-pattern>
      <http-method>GET</http-method>
    </web-resource-collection>
    <!-- https -->
    <user-data-constraint>
      <transport-guarantee>CONFIDENTIAL</transport-guarantee>
    </user-data-constraint>
  </security-constraint>
</web-app>

This is minimal web.xml for this task - web-resource-collection just defines URL pattern and HTTP method(s) we want to put a constraint on and user-data-constraint defines that constraint, which is in our case transport-guarantee. More information about these properties and security settings for web application can be found in Oracle Java EE 7 Tutorial.

I
have some simple webpage attached as well, so I can test my endpoint
right away. You can find it (along with complete project) in Tyrus workspace: [webpage] [whole project].

After
deploying this application to Glassfish Application Server, you should
be able to hit it using your favorite browser. URL where my application
resides is https://localhost:8181/sample-echo-https/
(may be different, depends on other configuration). My browser warns me
about untrusted certificate (I use what freshly built Glassfish
provides - self signed certificates) and after adding an exception for
this site, I can see my webpage and I am able to securely connect to wss://localhost:8181/sample-echo-https/echo.

WebSocket client

Already mentioned demo application
also contains test client, but execution of this is skipped for normal
build. Reason for this is that Glassfish uses these self-signed "random"
untrusted certificates and you are (in most cases) not able to connect
to these services without any additional settings.

Creating test
WebSocket client is actually quite similar to server side, only
difference is that you have to somewhere create client container and
invoke connect with some additional info. Java API for WebSocket allows
you to use annotated and programmatic way to construct endpoints. Server
side shows the annotated case, so let's see how the programmatic
approach will look.

final WebSocketContainer client = ContainerProvider.getWebSocketContainer();
client.connectToServer(new Endpoint() {
  @Override
  public void onOpen(Session session, EndpointConfig EndpointConfig) {
    try {
      // register message handler - will just print out the
      // received message on standard output.
      session.addMessageHandler(new MessageHandler.Whole<String>() {
      @Override
        public void onMessage(String message) {
         System.out.println("### Received: " + message);
        }
      });
      // send a message
      session.getBasicRemote().sendText("Do or do not, there is no try.");
    } catch (IOException e) {
      // do nothing
    }
  }
}, ClientEndpointConfig.Builder.create().build(),
   URI.create("wss://localhost:8181/sample-echo-https/echo"));

This
client should work with some secured endpoint with valid certificated
signed by some trusted certificate authority (you can try that with wss://echo.websocket.org).
Accessing our Glassfish instance will require some additional settings.
You can tell Java which certificated you trust by adding
-Djavax.net.ssl.trustStore property (and few others in case you are
using linked sample).

Complete command line when you are testing your service might need to look somewhat like:

mvn clean test -Djavax.net.ssl.trustStore=$AS_MAIN/domains/domain1/config/cacerts.jks\
-Djavax.net.ssl.trustStorePassword=changeit -Dtyrus.test.host=localhost\
-DskipTests=false

Where AS_MAIN points to your Glassfish instance.

Note: you might need to setup keyStore and trustStore per client instead of per JVM; there is a way how to do it, but it is Tyrus proprietary feature: http://tyrus.java.net/documentation/1.2.1/user-guide.html#d0e1128.

And that's it! Now nobody is able to "hear" what you are sending to or receiving from your WebSocket endpoint.

There
is always room for improvement, so the next step you might want to take
is introduce some authentication mechanism (like HTTP Basic or Digest).
This topic is more about container configuration so I'm not going to go
into details, but there is one thing worth mentioning: to access
services which require authorization, you might need to put this
additional information to HTTP headers of first (Upgrade) request (there
is not (yet) any direct support even for these fundamental mechanisms,
user need to register Configurator and add headers in beforeRequest method invocation). I filed related feature request as TYRUS-228; feel free to comment/vote if you need this functionality.

Join the discussion

Comments ( 6 )
  • guest Friday, August 2, 2013

    Constraint looks wrong. Listing GET means all other methods are not constrained. So a non SSL request with another verb might allow bypass. Check "verb tampering"


  • Pavel Friday, August 2, 2013

    Well, websocket protocol uses only "GET" method, so it is ok (at least for this simple case). But thanks for pointing that out, it could help others when developing some "real-world" applications.


  • guest Thursday, October 24, 2013

    Hi,

    I'm using glassfish 4.0 to develop an application that uses websockets. Everything works fine without ssl

    var webSocket = new WebSocket('ws://mydomain:8080/websocketserver/websocket/sessionXXX');

    However, when i try to use ssl the server does not execute the @OnOpen method and the client shows readyState = 3. Connection is never opened. This is what i'm using to open the connection on the client side.

    var webSocket = new WebSocket('wss://mydomain:8181/websocketserver/websocket/sessionXXX');

    I've tried a test page on https://mydomain/websocketserver/index.html and ssl is working fine with http. Thus, where can the problem be?

    Thanks


  • Pavel Bucek Thursday, October 31, 2013

    Hello,

    it might be lot of things. Best way to debug is to open your browsers javascript console, it usually contains some useful error message. Most of the time it is about SSL certificates - that can be always fixed by adding your server certificate to browsers test store.

    Also you could try sample-echo-https from Tyrus workspace, just to be sure that you don't have anything wrong in your app. Feel free to send a note to users@tyrus.java.net, you should get prompt answer there.

    Regards,

    Pavel


  • guest Tuesday, May 12, 2015

    how can I import to android?


  • Pavel Tuesday, May 12, 2015

    Hi Guest,

    please take a look at article "WebSocket Client on Android – Tyrus" (https://blogs.oracle.com/PavelBucek/entry/websocket_client_on_android_tyrus) .. there is a link to github repo with sample project.

    Regards,

    Pavel


Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.