It seems there are two ways to add authentication to Jersey REST apis
1) You can add a servlet filter.
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
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);
}
}
}
Thank you, your article save me :)
ReplyDeleteDo you have a complete code example of your approach which you can publish?
ReplyDeleteJohn 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.
DeleteHi. How do you get session from ContainerRequest
DeleteYou have to inject the httpServletRequest in your authfilterfactory and then get session from it.
Delete@Context
private HttpServletRequest httpServletRequest;
Thanks for posting this article.It really saved my day. Also I have found this article here describe the usage of jersey resource filter .
ReplyDeletehttp://goo.gl/Pm7ls
what library need to be imported for BasicAuthHelper?
ReplyDeleteBasicAuthHelper 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.
ReplyDeleteHi, 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
ReplyDeletein 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.
DeleteThank 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)
Deletetherefore perhaps there is a request scope variable in jersey that contains invoked abstact method information. Once again thank you for your help
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