Part-2: Tying in with the Java EE Platform

This part, covers integration with

  • Servlet, and

  • Java EE Security

Servlets and WebSockets

A WebSocket connection begins with a HTTP handshake. There is an association between javax.servlet.http.HTTPSession and javax.websocket.Session. The HTTPSession object can be obtained by overriding the modifyHandshake method in ServerEndpointConfig.Configurator. Once we have the it, the ServletContext can be easily retrieved

public class CustomServerEndpointConfigurator extends ServerEndpointConfig.Configurator{
    ...
    @Override
    public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {

        HttpSession httpSession = (HttpSession) request.getHttpSession();
        ServletContext servletContext = httpSession.getServletContext();
        ...
    }
}

Lifetime of Session-HTTPSession association

The WebSocket Session is closed if the HTTPSession dies (due to invalidation, timeout, user logging out etc.) - but this happens only in case the WebSocket endpoint happens to be a protected resource (more on this in the next section). The container is not obliged to kill the underlying WebSocket connection in response of closing an HTTPSession if it was not protected to begin with

Integrating with Java EE Security

The WebSocket specification does not define it's own security primitives. It relies heavily on the web container (Servlet) security model can be broadly divided into declarative and programmatic styles (in terms of their usage patterns). We will explore these strategies in this sub-section

Recommended: for a deep dive into Servlet security, please refer chapters 13, 14 of the latest (3.1) Servlet specification

Declarative security

The declarative Java EE security model is driven by the web.xml descriptor. WebSocket endpoints can benefit from the following security features - Authentication, Authorization and Secure transport. Let's look at each one of these

Authentication

It is driven by the login-config element in web.xml descriptor and the possible values are - BASIC, FORM, DIGEST and CLIENT-CERT

<login-config>
    <auth-method>BASIC</auth-method>
    <realm-name>file</realm-name>
</login-config>

To protect a WebSocket endpoint, the encapsulating resource (which acts as the WebSocket client) must be protected. A good example is a HTML page which uses the WebSocket Javascript client API to initiate a connection to the server endpoint. Once the authentication challenge is satisfied (depending upon the chosen method), the authenticated context is associated with the subsequent calls from the client to the WebSocket endpoint. Failure to authenticate results in a HTTP 401 error

Authorization

For declarative authorization to work, the <auth-constraint> settings (in tandem with other container configuration) is important. Post authentication, the authorization will kick in and only if the authenticated principal is found to be a member of the configured roles (this calculation will happen as per the application server specific setting), will the protected resource (and the WebSocket endpoint) be accessible. If not, the process will result in a HTTP 403 error

<auth-constraint>
    <description></description>
    <role-name>premium_users</role-name>
</auth-constraint>

Secure transport

This setting determines whether or not the communication channel is secure. From a WebSocket perspective, this is achieved by multiple configuration

  • the original (protected) resource should be accessed over https

  • the WebSocket URL must begin with wss (not ws) e.g. wss://we-all-chat/

  • <user-data-constraint> configuration in web.xml should be configured

<user-data-constraint>
    <transport-guarantee>
      CONFIDENTIAL
    </transport-guarantee>
</user-data-constraint>

Programmatic

The programmatic aspects of security handling are taken care of by Session and HandshakeRequest objects. These are passive in nature i.e. these come into picture after the primary level of authentication (HTTP based) is over which provide methods to access the Principal object, determine whether the user is part of a specific role and whether the communication is taking place over an encrypted connection

HandshakeRequest

You can check the Principal as well as check for role membership of a user using the getUserPrincipal and the isUserInRole methods respectively

public class SecurityConfigurator extends ServerEndpointConfig.Configurator {

    @Override
    public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
        System.out.println("Endpoint accessed by user - "+ req.getUserPrincipal().getName());
        String roleName = "premium-users";
        System.out.println("Is user in role "+ roleName + " ? " + req.isUserInRole(roleName));
    }
}

Session

As with HandshakeRequest, the Session object also provides access to the authenticated Principal via the getUserPrincipal method along with the isSecure API to check whether the communication is taking place over secure (encrypted channel) i.e. wss

Next chapter ..

.. looks at some of the Concurrency semantics of common WebSocket APIs

Last updated