Securing Jersey REST Services: Basic Authentication

Snippet of programming code in IDE
Published on

Securing Jersey REST Services: Basic Authentication

When it comes to building secure Jersey REST services, implementing authentication is of utmost importance. In this article, we will explore how to secure Jersey REST services using Basic Authentication, a simple yet effective method for protecting resources.

What is Basic Authentication?

Basic Authentication is a simple authentication scheme built into the HTTP protocol. It involves sending a username and password with each request. While it is not the most secure method on its own, when used over HTTPS, Basic Authentication can provide a reasonable level of security for your REST services.

Setting up a Jersey Project

Before we dive into securing our REST services, let's set up a simple Jersey project. You can use Maven to create a new project using the following command:

mvn archetype:generate -DarchetypeArtifactId=jersey-quickstart-grizzly2 -DarchetypeGroupId=org.glassfish.jersey.archetypes -DinteractiveMode=false -DgroupId=com.example -DartifactId=my-jersey-app -Dpackage=com.example -DarchetypeVersion=2.34

This will generate a basic Jersey project with the necessary dependencies.

Implementing Basic Authentication

To secure our Jersey REST services using Basic Authentication, we need to create a class that extends ContainerRequestFilter. This class will intercept incoming requests and validate the credentials provided.

Let's create a new class called BasicAuthFilter:

import java.io.IOException;
import java.util.Base64;
import java.util.StringTokenizer;

import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;

@Provider
public class BasicAuthFilter implements ContainerRequestFilter {

    private static final String AUTHORIZATION_HEADER = HttpHeaders.AUTHORIZATION;
    private static final String AUTHORIZATION_HEADER_PREFIX = "Basic ";

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        String authHeader = requestContext.getHeaderString(AUTHORIZATION_HEADER);

        if (authHeader != null && authHeader.startsWith(AUTHORIZATION_HEADER_PREFIX)) {
            String credentials = authHeader.substring(AUTHORIZATION_HEADER_PREFIX.length()).trim();
            byte[] decodedCredentials = Base64.getDecoder().decode(credentials);
            String decodedString = new String(decodedCredentials);
            StringTokenizer tokenizer = new StringTokenizer(decodedString, ":");
            String username = tokenizer.nextToken();
            String password = tokenizer.nextToken();

            // Validate username and password (e.g., against a database)
            if (isValidUser(username, password)) {
                return;
            }
        }

        // If the credentials are invalid, send a 401 Unauthorized response
        requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED)
                .entity("User cannot access the resource.").build());
    }

    private boolean isValidUser(String username, String password) {
        // Implement your validation logic here (e.g., check against a database)
        return "admin".equals(username) && "admin123".equals(password);
    }
}

In the above code, we define a BasicAuthFilter class that implements the ContainerRequestFilter interface. Within the filter method, we extract the Authorization header from the request and validate the credentials. If the credentials are valid, the request proceeds; otherwise, we send a 401 Unauthorized response.

Registering the Filter

To apply the BasicAuthFilter to our REST services, we need to register it with Jersey. We can do this by adding the @Provider annotation to the BasicAuthFilter class. Jersey will automatically discover this class and apply it to all incoming requests.

import org.glassfish.jersey.server.ResourceConfig;

public class MyApplication extends ResourceConfig {

    public MyApplication() {
        register(BasicAuthFilter.class);
        packages("com.example.resources"); // Define your package for REST resources
    }
}

In the MyApplication class, which extends ResourceConfig, we register our BasicAuthFilter by calling the register method. We also specify the package where our REST resources are located using the packages method.

Testing the Basic Authentication

Now that we have implemented Basic Authentication in our Jersey REST services, let's test it using a simple REST resource.

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;

@Path("secured")
public class SecuredResource {

    @GET
    @Path("hello")
    public Response getSecureHello() {
        return Response.status(Response.Status.OK)
                .entity("Hello, secured world!")
                .build();
    }
}

In the SecuredResource class, we define a simple GET endpoint /secured/hello. When a client makes a request to this endpoint, the BasicAuthFilter will intercept the request and validate the credentials before allowing access to the resource.

A Final Look

In this article, we have explored how to secure Jersey REST services using Basic Authentication. By implementing a custom ContainerRequestFilter and registering it with Jersey, we can validate user credentials for each incoming request. While Basic Authentication is a simple method, it can provide a basic level of security when used in conjunction with HTTPS.

By following the steps outlined in this article, you can ensure that your Jersey REST services are protected and accessible only to authorized users. Remember to handle sensitive credentials securely and consider additional security measures for production environments.

With Basic Authentication in place, your Jersey REST services are well-equipped to handle authentication and protect sensitive resources.

For further reading on Jersey and REST services, visit Jersey Documentation.

Happy coding!