In this Jersey rest security example, we will learn to secure Jersey REST APIs with basic authentication. This will make mandatory every user to provide username/password to authenticate into portal. Also, user must have certain level of role as well. I have extended this example from my other example created for RESTEasy API security and used ContainerRequestFilter implementation to verify access of user before he land on actual REST API.
Table of Contents 1. Create request authentication filter 2. Register AuthenticationFilter with ResourceConfig 3. Secure REST APIs 4. Test Jersey AuthenticationFilter
1. Create request authentication filter
We know that JAX-RS 2.0 has filters for pre and post request handling, so we will be using ContainerRequestFilter
interface. In this filter, we will get details of the method which request is trying to access.
We will find-out all security related configuration on that method, and verify everything here in this filter e.g. annotation like @PermitAll
, @DenyAll
or @RolesAllowed
.
According to annotation applied on methods, we will make the decision to pass or block the request.
package com.howtodoinjava.jersey.provider; import java.lang.reflect.Method; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.StringTokenizer; import javax.annotation.security.DenyAll; import javax.annotation.security.PermitAll; import javax.annotation.security.RolesAllowed; import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ResourceInfo; import javax.ws.rs.core.Context; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import javax.ws.rs.ext.Provider; import org.glassfish.jersey.internal.util.Base64; /** * This filter verify the access permissions for a user * based on username and passowrd provided in request * */ @Provider public class AuthenticationFilter implements javax.ws.rs.container.ContainerRequestFilter { @Context private ResourceInfo resourceInfo; private static final String AUTHORIZATION_PROPERTY = "Authorization"; private static final String AUTHENTICATION_SCHEME = "Basic"; @Override public void filter(ContainerRequestContext requestContext) { Method method = resourceInfo.getResourceMethod(); //Access allowed for all if( ! method.isAnnotationPresent(PermitAll.class)) { //Access denied for all if(method.isAnnotationPresent(DenyAll.class)) { requestContext.abortWith(Response.status(Response.Status.FORBIDDEN) .entity("Access blocked for all users !!").build();); return; } //Get request headers final MultivaluedMap<String, String> headers = requestContext.getHeaders(); //Fetch authorization header final List<String> authorization = headers.get(AUTHORIZATION_PROPERTY); //If no authorization information present; block access if(authorization == null || authorization.isEmpty()) { requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED) .entity("You cannot access this resource").build()); return; } //Get encoded username and password final String encodedUserPassword = authorization.get(0).replaceFirst(AUTHENTICATION_SCHEME + " ", ""); //Decode username and password String usernameAndPassword = new String(Base64.decode(encodedUserPassword.getBytes()));; //Split username and password tokens final StringTokenizer tokenizer = new StringTokenizer(usernameAndPassword, ":"); final String username = tokenizer.nextToken(); final String password = tokenizer.nextToken(); //Verifying Username and password System.out.println(username); System.out.println(password); //Verify user access if(method.isAnnotationPresent(RolesAllowed.class)) { RolesAllowed rolesAnnotation = method.getAnnotation(RolesAllowed.class); Set<String> rolesSet = new HashSet<String>(Arrays.asList(rolesAnnotation.value())); //Is user valid? if( ! isUserAllowed(username, password, rolesSet)) { requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED) .entity("You cannot access this resource").build();); return; } } } } private boolean isUserAllowed(final String username, final String password, final Set<String> rolesSet) { boolean isAllowed = false; //Step 1. Fetch password from database and match with password in argument //If both match then get the defined role for user from database and continue; else return isAllowed [false] //Access the database and do this part yourself //String userRole = userMgr.getUserRole(username); if(username.equals("howtodoinjava") && password.equals("password")) { String userRole = "ADMIN"; //Step 2. Verify user role if(rolesSet.contains(userRole)) { isAllowed = true; } } return isAllowed; } }
2. Register AuthenticationFilter with ResourceConfig
Now you will need to register above filter with ResourceConfig
instance. So create an instance like below:
package com.howtodoinjava.jersey; import org.glassfish.jersey.filter.LoggingFilter; import org.glassfish.jersey.server.ResourceConfig; import com.howtodoinjava.jersey.provider.AuthenticationFilter; import com.howtodoinjava.jersey.provider.GsonMessageBodyHandler; public class CustomApplication extends ResourceConfig { public CustomApplication() { packages("com.howtodoinjava.jersey"); register(LoggingFilter.class); register(GsonMessageBodyHandler.class); //Register Auth Filter here register(AuthenticationFilter.class); } }
And add this resource config in web.xml file.
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> <servlet> <servlet-name>jersey-serlvet</servlet-name> <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class> <init-param> <param-name>javax.ws.rs.Application</param-name> <param-value>com.howtodoinjava.jersey.CustomApplication</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>jersey-serlvet</servlet-name> <url-pattern>/rest/*</url-pattern> </servlet-mapping> </web-app>
3. Secure REST APIs
Now it’s time to secure the REST APIs. Use standard JAX-RS annotations for that like below.
@Path("/employees") public class JerseyService { @RolesAllowed("ADMIN") @GET @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) public Employees getAllEmployees() { Employees list = new Employees(); list.setEmployeeList(new ArrayList<Employee>()); list.getEmployeeList().add(new Employee(1, "Lokesh Gupta")); list.getEmployeeList().add(new Employee(2, "Alex Kolenchiskey")); list.getEmployeeList().add(new Employee(3, "David Kameron")); return list; } }
4. Test Jersey AuthenticationFilter
Let’s test if authentication settings are working or not.
HIT URL : http://localhost:8080/JerseyDemos/rest/employees

Pass username and password in basic auth parameters: howtodoinjava/password

Click below link to download the sourcecode for jersey rest api authentication example application.
Happy Learning !!
David Obber
Nice and clear article!
I’ve some questions about abortWith() method. Is there a difference between using it instead of throwing an exception? Using abortWith() is considered a better practice? if yes, why?
David
Daniel Hall
This is the best and cleanest example that I’ve found for creating a ContainerRequestFilter.
BUT…
Please fix the original code in the post to not use the static final responses.
People are not reading down through all of the comments to find the list of fixes that need to be made to the code in the original publication.
Please see: https://stackoverflow.com/questions/52714946/jersey-java-lang-illegalstateexception-the-output-stream-has-already-been-clos
Where somebody else used your example and got burned by the static final responses.
Lokesh Gupta
Thanks for the feedback. Updated the post.
Estuardo
For your information!
Issue about this code:
https://stackoverflow.com/questions/38484752/jersey-throws-npe-for-each-call-after-using-containerrequestcontext-abortwith
sandeep
Hi Lokesh
Its very Helpful Tutorial
But where you generate token for client
End how REST end point validate it
sandeep
And Also I want to set my login form
JSON data //{username:username,password:password}//
to REST web service through Angularjs
How my REST web service issue token and get back to client that it should succesfully request any response class
From REST client in FIREFOX it worked welll
BUT what about above scenario
deejay
Hi,
Its a very nice and helpful article. I tries running this on my local.
Could you please help me out how can I pass username and password?
What I am doing right now is trying to call the following url “http://localhost:8082/JerseyDemos/rest/employees”, but not getting a way to pass authentication?
Lokesh Gupta
Use base64encode.org to get encoded base64 encoded username:password combination. e.g. this example uses
howtodoinjava:password
which encoded value isaG93dG9kb2luamF2YTpwYXNzd29yZA==
. Pass this value in HTTP header Authorization.Danilo
Help: In url acesse return above error:
SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data
Lokesh Gupta
Please validate JSON in online json editor.
Bapa
Hi Lokesh,
Thanks for sharing this information, But i am getting a null pointer exception for
private ResourceInfo ResourceInfo;
@Override
public void filter(ContainerRequestContext request) throws IOException
{
Method method = ResourceInfo.getResourceMethod();
SEVERE: Servlet.service() for servlet [jersey-serlvet] in context with path [/RestSimple] threw exception [java.lang.NullPointerException] with root cause
java.lang.NullPointerException
Pedro Sanders
Beautifully implemented. Thanks!
internetms52
thanks for sharing, It is very helpful.
IVO
WHERE IS THE CLASS GsonMessageBodyHandler?
Lokesh Gupta
Please see inside attached source code.
Sanjay
Hi, I am working with legacy code and in my web.xml it’s already has an entry of
javax.ws.rs.Application
com.howtodoinjava.jersey.LeacagcyApplication
With older version of jersery-server jar.
Now I want add a new Application consist of RolesAllowedDynamicFeature register call.
How can I achieve that.
Thanks in advance
Jeno
Thanks for sharing!
Just one comment
you can use
javax.ws.rs.core.HttpHeaders.AUTHORIZATION
instead of
private static final String AUTHORIZATION_PROPERTY = “Authorization”;
Mabi
Great article, thanks.
What if I’d like to use the same credentials I got in my AuthenticationFilter class to authenticate against my resource (database, application, .. etc) to get the data.
Simply how to inject the username and password to my Resource class?
Amit
how to use post method in jerseyservice
what we have to put value in post method
Palash Rawat
If a user requests for an information like user account information by using his user id then how to authorize him to access that information (How to check than the information requested by the user is his own information and not the different user’s information) and authorize him to access such information.
Lokesh Gupta
You will need to implement some kind of domain object security.
Juan Calderón
Perfect!. Besides the Method, you can also check for the Type with Class clase = resourceInfo.getResourceClass().
Gary
When I try and inject the ResourceInfo into the AuthenticationFilter I get the following exception:
A MultiException has 3 exceptions. They are:
1. java.lang.IllegalStateException: Not inside a request scope.
2. java.lang.IllegalArgumentException: While attempting to resolve the dependencies of example.jersey.MyRequestFilter errors were found
3. java.lang.IllegalStateException: Unable to perform operation: resolve on example.jersey.MyRequestFilter
Lokesh Gupta
Use Jersey 2.5.1 if possible OR inject ResourceInfo using @Context javax.inject.Provider <ResourceInfo> in 2.4
Mahdi Jaghoori
Thanks for the good example. Although in case no user-pass is provided, your code simply responses “access denied” while I would like to have the browser show the login page. I got it working by changing the corresponding part of the code as follows:
if(authorization == null || authorization.isEmpty())
{
// requestContext.abortWith(ACCESS_DENIED);
requestContext.abortWith(Response.status( Response.Status.UNAUTHORIZED ).header(“WWW-Authenticate”, “Basic”).build());
return;
}
Numa Kuri
Hi , as logged by Nav getting org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException while executing the client included genson-1.3.jar in classpath but the error is still there , any pointers ?
Kim
thx for sharing dear friend
Carlos
Hi, I’m trying to test it and always I can have access to the application. Always a give a 200 OK message. How I have to test it to receive the 401 unauthorized message????
Scott
See my response below for the lines you need to change to fix this.
Georg.Haupt
Nice Example, very easy and good structured.
But when you run it in TOMCAT you will find out, that the second attempt to read an unauthorized resource does not result in an error message.
This is a side effect, because the Responses for ACCESS_FORBIDDEN or ACCESS_DENIED are defined as static final.
Instead the above code write:
…
if(method.isAnnotationPresent(DenyAll.class))
{
requestContext.abortWith(Response.status(Response.Status.FORBIDDEN)
.entity(“Access blocked for all users !!”).build());
return;
}
…
Naveen
Thanks Lokesh .Nice . And thank you Georg for correction. It worked well using RESTClient,
I am facing below issue while calling the ws using the class/client code at: com.howtodoinjava.jersey.client.JerseyClientExamples
Web service I have deployed in Tomcat and running the standalone code from JerseyClientExamples:
Exception which I am getting:
Exception in thread “main” javax.ws.rs.ProcessingException: java.net.ConnectException: Connection refused
at org.glassfish.jersey.client.HttpUrlConnector.apply(HttpUrlConnector.java:244)
at org.glassfish.jersey.client.ClientRuntime.invoke(ClientRuntime.java:254)
at org.glassfish.jersey.client.JerseyInvocation$1.call(JerseyInvocation.java:671)
at org.glassfish.jersey.client.JerseyInvocation$1.call(JerseyInvocation.java:668)
at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
at org.glassfish.jersey.internal.Errors.process(Errors.java:228)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:444)
at org.glassfish.jersey.client.JerseyInvocation.invoke(JerseyInvocation.java:668)
at org.glassfish.jersey.client.JerseyInvocation$Builder.method(JerseyInvocation.java:402)
at org.glassfish.jersey.client.JerseyInvocation$Builder.get(JerseyInvocation.java:302)
at com.howtodoinjava.jersey.client.JerseyClientExamples.httpGETCollectionExample(JerseyClientExamples.java:47)
at com.howtodoinjava.jersey.client.JerseyClientExamples.main(JerseyClientExamples.java:26)
Caused by: java.net.ConnectException: Connection refused
Florin
Nice catch. I saw the same issue when I tried it.
Nav
Hi,
While running the standalone class com.howtodoinjava.jersey.client.JerseyClientExamples
getting below exception:
Exception in thread “main” org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException: MessageBodyReader not found for media type=text/html;charset=utf-8, type=class com.howtodoinjava.jersey.beans.Employees, genericType=class com.howtodoinjava.jersey.beans.Employees.
Scott
I ran into the same issue, the problem is that a Static Response is reused (i.e. the same Response object is re-used)… this is a bad / invalid practice and a new Response object should be created with each response.
To properly create a new response each time, in the filter you should replace this line:
requestContext.abortWith(ACCESS_DENIED);
With:
requestContext.abortWith( Response.status(Response.Status.UNAUTHORIZED)
.entity(“You cannot access this resource”).build() );
And you can remove the “ACCESS_DENIED” object since that is no good.
To reuse the same response multiple times you can create a helper method like this:
private Response unauthorizedResponse(){
return Response.status(Response.Status.UNAUTHORIZED)
.entity(“You cannot access this resource”).build();
}
Then simply use:
requestContext.abortWith( unauthorizedResponse() );
The same setup should be used on the “ACCESS_FORBIDDEN” object, and that object should also be removed.
Source of discussion that lead to my solution:
https://github.com/jtmelton/appsensor/issues/30
Lokesh Gupta
Thanks for sharing your solution with us. Much appreciated.