Jersey (JAX-RS) implements a HTTP Basic Auth decoder (for 2.x branch)

Here is the updated version of the article about how to implement a HTTP Basic Auth Filter, for Jersey 2.x.

The global idea remains the same, so check out the previous article about how and why we do this.

Jersey 2.x and filters

The Jersey project, when switching to 2.x branch, have fully changed it’s internal package names. Therefore, this article has first to update that:

Java built-in server
ResourceConfig rc = new PackagesResourceConfig("");
rc.getProperties().put(
	"jersey.config.server.provider.packages",
	"org.glassfish.jersey.filter;com.myprogram.filters.AuthFilter"
);
rc.getProperties().put(
	"javax.ws.rs.container.ContainerRequestFilter",
	"org.glassfish.jersey.filter.LoggingFilter;com.myprogram.filters.AuthFilter"
);

HttpServer server = HttpServerFactory.create("http://localhost:9999/", rc);
server.start();
Tomcat
<init-param>
	<param-name>jersey.config.server.provider.packages</param-name>
	<param-value>org.glassfish.jersey.filter;com.myprogram.filters.AuthFilter</param-value>
</init-param>
<init-param>
	<param-name>javax.ws.rs.container.ContainerRequestFilter</param-name>
	<param-value>org.glassfish.jersey.filter.LoggingFilter;com.myprogram.filters.AuthFilter</param-value>
</init-param>

Ok, quite the same except packages changes. Note that, I’m adding « jersey.config.server.provider.packages » parameter, I’ve found that Jersey needs it now for almost everything (on Jersey 1.x on filters, it was OK to skip it).

You need of course to have somewhere the BasicAuth class we’ve created in previous article, this does not change.

Jersey 2.x and filters lifetime

New concept introduced with new Jersey 2.x, a kind of lifetime filter. Right now, if you adapt the previous article code, you will see the filter kind of « don’t work », this is because the filter system now runs in parallel of the resource code. Which means, for a Auth filter, that user will be able to access data at the same time you check the security access of it. Should be OK in many cases, but here it’s really not: we are appending to request our user data.
It does mean that a resource will have access to request BEFORE we append the user to it: we can’t check any security during the resource consume. Definitely not something we want here.

Lucky for us, Jersey has a new annotation PreMatching, basically saying « must run before », exactly what we want:

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.container.PreMatching;

/**
 * Jersey HTTP Basic Auth filter
 * @author Deisss (LGPLv3)
 */
@Provider
@PreMatching
public class AuthFilter implements ContainerRequestFilter {
	/**
	 * Apply the filter : check input request, validate or not with user auth
	 * @param containerRequest The request from Tomcat server
	 */
	@Override
	public void filter(ContainerRequestContext containerRequest) throws WebApplicationException {
		//GET, POST, PUT, DELETE, ...
		String method = containerRequest.getMethod();
		// myresource/get/56bCA for example
		String path = containerRequest.getUriInfo().getPath(true);

		//We do allow wadl to be retrieve
		if(method.equals("GET") && (path.equals("application.wadl") || path.equals("application.wadl/xsd0.xsd")){
			return;
		}

		//Get the authentification passed in HTTP headers parameters
		String auth = containerRequest.getHeaderString("authorization");

		//If the user does not have the right (does not provide any HTTP Basic Auth)
		if(auth == null){
			throw new WebApplicationException(Status.UNAUTHORIZED);
		}

		//lap : loginAndPassword
		String[] lap = BasicAuth.decode(auth);

		//If login or password fail
		if(lap == null || lap.length != 2){
			throw new WebApplicationException(Status.UNAUTHORIZED);
		}

		//DO YOUR DATABASE CHECK HERE (replace that line behind)...
		User authentificationResult =  AuthentificationThirdParty.authentification(lap[0], lap[1]);

		//Our system refuse login and password
		if(authentificationResult == null){
			throw new WebApplicationException(Status.UNAUTHORIZED);
		}

		//TODO : HERE YOU SHOULD ADD PARAMETER TO REQUEST, TO REMEMBER USER ON YOUR REST SERVICE...

		return;
	}
}

The same guideline still applies here:

  • If you have some request which must pass authentification, you should put them next to « application.wadl »
  • The authentificationThirdParty is a class I invent here to move the login/password check outside that class, you can of course put what you want (I would suggest here to add resource and method to make a kind of ACL control here)
  • Also, at the end I encourage you to add the retrieve user inside the request, to be able to retrieve it after the filter inside the REST app. Here is a the line you should put for Tomcat : « sr.setAttribute(« logged_user », authentificationResult);« , while sr is « @Context HttpServletRequest sr; » outside the function (inside class somewhere)

The last point should be something you want in many cases, and is why we use the PreMatching annotation.

Publicités

7 Commentaires

  1. Maddin

    Hey Thanks a lot for your nice example!

    I don’t know where to put the sr.setAttibute in Tomcat and how to make a connection to the sr object in the code, how you describe here:
    « Here is a the line you should put for Tomcat : « sr.setAttribute(« logged_user », authentificationResult);« , while sr is « @Context HttpServletRequest sr; » outside the function (inside class somewhere) »

    My main question is: How should i store the user in the request for the later use in the REST app?
    Is it an appropriate way to store it in the SecurityContext? In the jersey tutorial they use it like that:

    public ShoppingBasketResource get(@Context SecurityContext sc) {
    if (sc.isUserInRole(« PreferredCustomer ») {
    return new PreferredCustomerShoppingBasketResource();
    } else {
    return new ShoppingBasketResource();
    }
    }

    Or is it possible to use the @RolesAllowed(« user ») Annotation in Jersey? That would be really awesome. Here is how they do it in the Jersey example. I can’t make it work. :-/
    @Path(« / »)
    @PermitAll
    public class Resource {
    @RolesAllowed(« user »)
    @GET
    public String get() { return « GET »; }

    • deisss

      I need to dig into that, personally I was adding to the request a parameter, but maybe the securityContext is a much better approach.

      But by default:
      @Context
      Means you can add it during filter process, and retrieve it later, without any special processing on your side… Will search for it, could be quite interesting 😉

  2. deisss

    Here is the article about security context in action:
    https://simplapi.wordpress.com/2015/09/19/jersey-jax-rs-securitycontext-in-action/

    I’ve just tested it, works well, so if you found any problem tell me 😉
    I’ve also reworked a little bit this article (some old Jersey 1.x elements was still there…)

    To store the user as you ask this also, here is a pretty quick example. Please note it works only in Tomcat (but others should have something similar to do it):

    public class AuthFilter implements ContainerRequestFilter {
    @Context
    private transient HttpServletRequest sr;

    public void filter(ContainerRequestContext request) throws WebApplicationException {
    // AFTER YOU GOT USER FROM DATABASE
    //Adding user data (most important one), to request
    this.sr.setAttribute(« logged_user », user);
    }
    }

    Inside a resource:
    import javax.servlet.http.HttpServletRequest;

    public class TestResource {
    @Context
    private HttpServletRequest request;

    @Path(« test »)
    public void test() {
    User = (User) request.getAttribute(« logged_user »);
    }
    }

    But with the security context, it’s probably better to keep it instead of this, this can still be handy in some cases, like retrieving the user ID, but extending SecurityContext to support this, is maybe better 😉

  3. ernirulez

    Hi, just one thing. You are wrong when you say « .. the filter system now runs in parallel of the resource code ». That´s not actually true.
    There is a matching process, where the request matches to a certain method of your resource. The annotation @PreMatching just makes the filter to execute even before that matching process. However, if you don´t use that annotation, the filter still executes BEFORE the matched method.

  4. ernirulez

    I forgot saying.. thank you very much for the article. It´s been really helpful. It´s a pity that the official Jersey documentation is a bit unclear and lacks some concepts.

  5. Davit

    what should i put in pom.xml to download the lib?

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s

%d blogueurs aiment cette page :