Fork me on GitHub

Arturo Volpe

I am a software engineer. Interested in open source, open data and devops.

BasicAuth filter with JaxRS

26 Oct 2015 » develop

Basic Auth can be a simple way to secure your web services, specially when it’s combined with SSL, to implement this kind of security with JaxRS, you need to create a ContainerRequestFilter and extract the header, check for the user and throw a exception if the user is invalid.

In this example we will use an simple stub to handle the authorization, for example, with this service:

public class SecurityChecker {

    public void check(String user, String pass) {
        if (realCheck(user, pass)) return;
        else throw new WrongUsernameAndPasswordException();

    }

    private boolean realCheck(String user, String pass) {
        // the real check here!
    }
}

The exception can extend WebApplicationException to handle the parsing to text, and have a nice message when the combination of user and password is wrong, for example:

public class WrongUsernameAndPasswordException extends WebApplicationException {

    public WrongUsernameAndPasswordException() {
        super(Response.status(Response.Status.UNAUTHORIZED)
         .type(MediaType.TEXT_PLAIN)
         .entity("Wrong user/password combination")
         .build());
    }
}

When this two classes in order, we can create the security filter, we need the @Provider and @PreMatching annotations to intercept the request before is handled for our methods annotated with @Path, also we need to extends from ContainerRequestFilter:

@Provider
@Priority(Priorities.AUTHENTICATION)
@PreMatching
public class SecurityFilter implements ContainerRequestFilter {

   // We inject the checker service.
   @Inject SecurityChecker checker;

   @Override
   public void filter(ContainerRequestContext requestContext) throws IOException {

      // Extract the actual header
      String header = requestContext.getHeaderString("Authorization");

      // check if is not empty or null
      if (header == null || header.trim().isEmpty()) throw new WrongUsernameAndPasswordException();

      // The format is: "Basic: base64(user:pass)"
      String[] parts = header.split(" ", 2);

      // if after the split we don't has two parts, the header is wrong.
      if (parts.length < 2) throw new WrongUsernameAndPasswordException();

      // Extract the "base64(user:pass)" part and decode
      String userPass = new String(Base64.getDecoder().decode(parts[1]));

      // Split with ":" to get the user and the pass
      String[] userPassArray = userPass.split(":", 2);

      // if after the split don't has two parts, the header is wrong.
      if (userPassArray.length < 2) throw new WrongUsernameAndPasswordException();

      String user = userPassArray[0];
      String pass = userPassArray[1];
      checker.check(user, pass);
   }

}