Basic HTTP authentication with Jersey / Grizzly

aioobe picture aioobe · Jan 30, 2013 · Viewed 13.8k times · Source

I've written a simple REST server using JAX-RS, Jersey and Grizzly. This is how I start the server:

URI baseUri = UriBuilder.fromUri("http://localhost/api")
                        .port(8081)
                        .build();

ResourceConfig rc = new PackagesResourceConfig("se.aioobe.resources");
HttpServer httpServer = GrizzlyServerFactory.createHttpServer(baseUri, rc);

Now I need to protect the resources using Basic HTTP authentication, and I can't figure out how to do this.

I can switch from Grizzly to for instance Jetty if it is simpler to get it to work, but I really value the simple configuration / start up that Grizzly provides.

I've read a lot of tutorials. They all mention the web.xml but in my current configuration I don't have one. (Do I need to add one for HTTP authentication?) I've found the following questions, neither of them is of any help :-(

(No SSL required at this point. The authentication is at this point just to prevent the public from peeking at our beta.)

TL;DR: How do I add basic HTTP authentication to a Jersey / Grizzly webapp?

Answer

aioobe picture aioobe · Jan 31, 2013

I managed to get it working after a couple of hours, based on this blog post.

My solution involves:

  • Maven artifacts:
    • jersey-server (v 1.17)
    • jersey-grizzly2 (v 1.17)
  • Hard coded username / password (replace with database lookup if you like)
  • No web.xml (programatically configured server)
  • No SSL involved

I created this ContainerRequestFilter:

public class AuthFilter implements ContainerRequestFilter {

    // Exception thrown if user is unauthorized.
    private final static WebApplicationException unauthorized =
       new WebApplicationException(
           Response.status(Status.UNAUTHORIZED)
                   .header(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\"realm\"")
                   .entity("Page requires login.").build());

    @Override
    public ContainerRequest filter(ContainerRequest containerRequest) 
            throws WebApplicationException {

        // Automatically allow certain requests.
        String method = containerRequest.getMethod();
        String path = containerRequest.getPath(true);
        if (method.equals("GET") && path.equals("application.wadl"))
            return containerRequest;

        // Get the authentication passed in HTTP headers parameters
        String auth = containerRequest.getHeaderValue("authorization");
        if (auth == null)
            throw unauthorized;

        auth = auth.replaceFirst("[Bb]asic ", "");
        String userColonPass = Base64.base64Decode(auth);

        if (!userColonPass.equals("admin:toHah1ooMeor6Oht"))
            throw unauthorized;

        return containerRequest;
    }
}

And I then changed the startup code to include the filter:

URI baseUri = UriBuilder.fromUri("http://localhost/api")
                        .port(8081)
                        .build();

ResourceConfig rc = new PackagesResourceConfig("se.aioobe.resources");

// Add AuthFilter ////////////
rc.getProperties().put("com.sun.jersey.spi.container.ContainerRequestFilters",
                       "<YOUR PACKAGE FOR AuthFilter>.AuthFilter");
//////////////////////////////

HttpServer httpServer = GrizzlyServerFactory.createHttpServer(baseUri, rc);