If you are creating REST-APIs in Java, you have probably already heard of JAX-RS. While JAX-RS may be implemented on top of Servlets, there is no requirement to do so. This also means that you may not be able to use Servlet-specific things like ServletFilters.

But don’t fear — JAX-RS provides a nice alternative and I’ll show you how you can easily implement an authentication filter that way. The interface is javax.ws.rs.container.ContainerRequestFilter, where we need to implement the filter() method, which we’ll do below. The other job we have to do is to decide where it will be applied in the chain of (possible) filters. Let’s have a look at the request flow:

Request flow through the filters

The central piece is the Resource Matching (green), where the engine decides from the request url and method with the help of your @Path and @GET annotations decides which Resource Method to call. When you create a request filter without further qualification it will be a PostMatch filter (red). If you want PreMatch (yellow), then you need to provide the @PreMatch annotation on you class.

When to use Pre Match vs Post Match?

To illustrate this, I have implemented a /test endpoint, that I am calling with one time pre-match and one time post-match for my authentication filter (without passing the authentication information). Let’s first look at Post-Match (default):

$ curl -i http://localhost:8080/test

HTTP/1.1 401 Unauthorized



$ curl -i http://localhost:8080/bad-path

HTTP/1.1 404 Not Found # Now with auth header

$ curl -i -Hx-auth:`cat cred.txt` http://localhost:8080/test

HTTP/1.1 200 OK

You can see that the default is to first run the resource matching and then then our request filter by returning the 404 Not Found for /bad-path. Likewise if I’d specify acceptable media-types that are not supported by the resource method, the engine would return a 406 code. Depending on your use case it may not be desirable to leak such information to non-authenticated users.

Now we look at the Pre-Match case (yellow in the diagram above):

$ curl -i

HTTP/1.1 401 Unauthorized curl -i http://localhost:8080/test HTTP/1.1Unauthorized $ curl -i

HTTP/1.1 401 Unauthorized curl -i http://localhost:8080/bad-path HTTP/1.1Unauthorized

Our filter is here in both times detecting that authorisation is failing and aborting the request early with a 401 response: the request does not even reach the Resource Matching (green), which is what we want.

What about filter ordering?

As indicated in the first diagram, it is possible to deploy more than one filter. While sometimes ordering does not matter, most often it does. For example take two filters that first check authentication and then load some data about the authenticated user from a database.

Filter ordering

To define the ordering, we can use the @Priority annotation.

@Priority(1)

@PreMatching

@Provider

public class AuthFilter .. {..}



@Priority(2)

@PreMatching

@Provider

public class DbLoadFilter .. {..}

Filters are ordered in ascending order of the priority value.

Enough bla bla — show me the code

Ok, here we go :-)

@PreMatching // (1)

@Provider // (2)

public class IncomingRequestFilter implements ContainerRequestFilter {



@Override

public void filter(ContainerRequestContext requestContext) throws IOException {



String xAuth = requestContext.getHeaderString("x-auth");



// No auth - abort

if (xAuth == null || xAuth.isEmpty()) { // (3)

requestContext.abortWith(Response.status(

Response.Status.UNAUTHORIZED).build());

return; // (4)

}

As described above we want a PreMatch filter (1). The @Provider annotation (2) is required for the JAX-RS engine to find our filter and correctly wire it into the filter chain. Within the filter() method we then retrieve the auth header and check if it is present and not empty (3). If the header is not present or empty, we abort the context with a 401 Unautorised status.

Abort the request if auth header is not present or bad

Finally we need to directly return (4), as just calling requestContext.abortWith() does not do that for us and the code flow would still run in to the following code.

// Now that we are sure that there is a header, we can use it.

Optional<XId> xid = HeaderHelper.getXIdFromString(xAuth); // (5)

if (xid.isPresent()) {

// header was good, so now create the security context

XId xIdentity = xid.get(); // (6)

SecurityContext sctx = new XIdSecurityContext(xIdentity);

requestContext.setSecurityContext(sctx);

} else {

// Header was present, but not correct // (7)

requestContext.abortWith(Response.status(

Response.Status.UNAUTHORIZED).build());

}

}

}

Now that the header was present, we can parse it (5). If parsing succeeds, we can create a javax.ws.rs.core.SecurityContext and stick it into the request (6), so that resource methods can later retrieve it via header injection for further processing. Constructing the SecurityContext will also set the callers principal in it for later use.

In case parsing failed we again abort the flow with a 401 response.

How to use the SecurityContext?

Our authentication header also provides additional information like the current user name, which may be needed later in the processing.

In our resource method we can use it in two ways:

@Path("/test")

@RequestScoped

public class TestService {



// Needed to retrieve the user

@Context SecurityContext ctx; // (7)





@Path("/")

@GET

public String test(@Context SecurityContext ctx2) { // (8)



System.out.println(ctx.getUserPrincipal().getName());

System.out.println(ctx2.getUserPrincipal().getName());

We can either inject the SecurityContext for the entire class (7) or on a per method basis (8) and e.g. retrieve the calling Principal from it.

There is of course room for improvement: we could directly make the Princpial available for injection, so that we don’t need to take it from the SecurityContext. For you can have a look at the full code of my IncomingRequestFilter and related classes on GitHub.