Friday, November 4, 2011

Jesey writing an authentication filter

It seems there are two ways to add authentication to Jersey REST apis

1) You can add a servlet filter.
public class RestAuthenticationFilter implements Filter {
    @Override
    public void destroy() {
        // TODO Auto-generated method stub        
    }
    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {     
    try {
           User user = BasicAuthHelper.authenticateUser(request);
            if (user == null) {
                response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
            } else {
                request.setAttribute("user", user);
                chain.doFilter(request, response);
            }
     } catch (ApplicationException e) {
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
     }
    }
    @Override
    public void init(FilterConfig config) throws ServletException {
    } 
} 


2) You can do it using the jersey filter. You have to implement a ResourceFilterFactory and handle the auth in ContainerRequestFilter. The detailed code is below.  I like the approach 1 as it give complete lifecycle control. However if you need more specifc things like accessing QueryParams or PathParams then approach 2 is the way to go


public class RestAuthFilterFactory implements ResourceFilterFactory {
    private static final AppLogger logger = AppLogger
            .getLogger(RestAuthFilterFactory.class);

    @Context
    private UriInfo uriInfo;

    @Override
    public List create(AbstractMethod method) {
        return Collections.singletonList((ResourceFilter) new Filter());
    }

    private class Filter implements ResourceFilter, ContainerRequestFilter {
        protected Filter() {
        }

        public ContainerRequestFilter getRequestFilter() {
            return this;
        }

        public ContainerResponseFilter getResponseFilter() {
            return null;
        }

        public ContainerRequest filter(ContainerRequest request) {
            logger.info("Url invoked is {}", uriInfo.getPath());
            String authHeader = request.getHeaderValue("Authorization");
            if (authHeader != null && authHeader.startsWith("Basic ")) {
                   User user = BasicAuthHelper.authenticateUser(request);
               if (user == null) {
                  throw new WebApplicationException(Response.Status.UNAUTHORIZED);
               }
            return request;
            }
              throw new WebApplicationException(Response.Status.UNAUTHORIZED);
        }
        }
}

12 comments:

  1. Thank you, your article save me :)

    ReplyDelete
  2. Do you have a complete code example of your approach which you can publish?

    ReplyDelete
    Replies
    1. John unfortunately the code has some internal details of the company I work for and I cant publish those. I would be though happy to answer any spefic questions you have.

      Delete
    2. Hi. How do you get session from ContainerRequest

      Delete
    3. You have to inject the httpServletRequest in your authfilterfactory and then get session from it.

      @Context
      private HttpServletRequest httpServletRequest;

      Delete
  3. Thanks for posting this article.It really saved my day. Also I have found this article here describe the usage of jersey resource filter .

    http://goo.gl/Pm7ls

    ReplyDelete
  4. what library need to be imported for BasicAuthHelper?

    ReplyDelete
  5. BasicAuthHelper is just a class written by me, the intent of the code was to just demonstrate the strucuture of writing a filter. Instead of BasicAuthHelper you can plug in your own class to authenticate the user.

    ReplyDelete
  6. Hi, thank you very much for sample code. May i question, why are you using new instance of Filter for each resource method? Are there some thread-safe issues here? Thank you very much in advance

    ReplyDelete
    Replies
    1. in this e.g. you could get away by reusing same filter but what if someone tomorrow comes and adds instance variables in the class then in that case there can be concurrency issues. so like ServletFilter even here I am creating a new instance.

      Delete
    2. Thank you very much for your response. Sorry for being so precise, but as far as i know there is only one instance of ServletFilter in JVM loaded (it's according to Servlet 2.4 Spec 6.2.1 https://jira.sakaiproject.org/secure/attachment/16135/servlet-2_4-fr-spec.pdf)
      therefore perhaps there is a request scope variable in jersey that contains invoked abstact method information. Once again thank you for your help

      Delete
    3. you dont really need to create a new instance for e.g. this url here the author is not create a new instance http://anismiles.wordpress.com/2012/03/02/securing-versioning-and-auditing-rest-jax-rs-jersey-apis/ of SecurityContextFilter in the factory. But I guess I had to use uriInfo that I was injecting in the factory and thats why I might have done it. I could have injected that in the filter also but not sure if its doable, will have to try it out.

      Delete