In this RESTful services tutorial, we will see about how to do HTTP basic authentication. There are many ways to implement authentication in RESTful web services. HTTP basic authentication is the first step in learning security.
In this tutorial, I have not used any Jersey specific interceptors and we will see about them in future tutorials. I will be using a standard servlet filter to intercept the request, do the authentication and continue with response.
Follow the earlier written tutorial, RESTful Web Services with Java JAX-RS using Jersey to setup a basic hello world RESTful service.
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>RESTful Service Authentication</display-name> <welcome-file-list> <welcome-file>index.htm</welcome-file> </welcome-file-list> <servlet> <servlet-name>RESTful Jersey Hello World Service</servlet-name> <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class> <init-param> <param-name>jersey.config.server.provider.packages</param-name> <param-value>com.javapapers.webservices.rest.jersey</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>RESTful Jersey Hello World Service</servlet-name> <url-pattern>/rest/*</url-pattern> </servlet-mapping> <filter> <filter-name>AuthenticationFilter</filter-name> <filter-class>com.javapapers.webservices.rest.jersey.RestAuthenticationFilter</filter-class> </filter> <filter-mapping> <filter-name>AuthenticationFilter</filter-name> <url-pattern>/rest/*</url-pattern> </filter-mapping> </web-app>
We have added a servlet filter class RestAuthenticationFilter
for all urls of the form /rest/*
.
package com.javapapers.webservices.rest.jersey; import java.io.IOException; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class RestAuthenticationFilter implements javax.servlet.Filter { public static final String AUTHENTICATION_HEADER = "Authorization"; @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain filter) throws IOException, ServletException { if (request instanceof HttpServletRequest) { HttpServletRequest httpServletRequest = (HttpServletRequest) request; String authCredentials = httpServletRequest .getHeader(AUTHENTICATION_HEADER); // better injected AuthenticationService authenticationService = new AuthenticationService(); boolean authenticationStatus = authenticationService .authenticate(authCredentials); if (authenticationStatus) { filter.doFilter(request, response); } else { if (response instanceof HttpServletResponse) { HttpServletResponse httpServletResponse = (HttpServletResponse) response; httpServletResponse .setStatus(HttpServletResponse.SC_UNAUTHORIZED); } } } } @Override public void destroy() { } @Override public void init(FilterConfig arg0) throws ServletException { } }
Before the RESTful resource is invoked, we get control in the above class. We get the HTTP Header
value for Authorization
. That’s the Basic authentication credential and we will pass it on to AuthenticationService
for validation.
package com.javapapers.webservices.rest.jersey; import java.io.IOException; import java.util.Base64; import java.util.StringTokenizer; public class AuthenticationService { public boolean authenticate(String authCredentials) { if (null == authCredentials) return false; // header value format will be "Basic encodedstring" for Basic // authentication. Example "Basic YWRtaW46YWRtaW4=" final String encodedUserPassword = authCredentials.replaceFirst("Basic" + " ", ""); String usernameAndPassword = null; try { byte[] decodedBytes = Base64.getDecoder().decode( encodedUserPassword); usernameAndPassword = new String(decodedBytes, "UTF-8"); } catch (IOException e) { e.printStackTrace(); } final StringTokenizer tokenizer = new StringTokenizer( usernameAndPassword, ":"); final String username = tokenizer.nextToken(); final String password = tokenizer.nextToken(); // we have fixed the userid and password as admin // call some UserService/LDAP here boolean authenticationStatus = "admin".equals(username) && "admin".equals(password); return authenticationStatus; } }
Here in we have standard Java code. We extract the username and password from the auth string. Then verify it against the existing credentials and return boolean accordingly.
package com.javapapers.webservices.rest.jersey; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; @Path("/helloworld") public class HelloWorld { @GET @Produces(MediaType.TEXT_PLAIN) public String sayPlainTextHello() { return "Hello World RESTful Jersey!"; } @GET @Produces(MediaType.TEXT_XML) public String sayXMLHello() { return "<?xml version=\"1.0\"?>" + "<hello> Hello World RESTful Jersey" + "</hello>"; } @GET @Produces(MediaType.TEXT_HTML) public String sayHtmlHello() { return "<html> " + "<title>" + "Hello World RESTful Jersey" + "</title>" + "<body><h1>" + "Hello World RESTful Jersey" + "</body></h1>" + "</html> "; } }
Above is a Jersey implementation of Java JAX-RS RESTful service. This should be invoked on successful authentication. If the authentication is failed we will get 401 status error given in the Servlet authentication filter.
I used “Advanced Rest Client”, a Chrome extension to test the RESTful services. Its nice and does the job well. Add it in your Chrome browser and send the request as below. Sending the basic auth credentials in this REST client is not so trivial. They should think about adding an explicit button. How we should do it is, go to the “Headers” tab and type “Authorization” in the key field and then go to the value field. On focus of value, we get a small pop-up button “Construct” and on clicking that we get the form to enter username and password.
On pop-up, enter the authentication credentials.
After adding credentials, ensure that the method selected is GET for this example tutorial and then send the request. On success, the REST resource should respond with hello world message.
Project zip does not include the dependent jar files. I have included all the JAR file from the Jax-RS Jersey download bundle in the lib folder. Please download all those jar files and add to the project lib. Following are those JAR files.
aopalliance-repackaged-2.4.0-b06.jar asm-debug-all-5.0.2.jar hk2-api-2.4.0-b06.jar hk2-locator-2.4.0-b06.jar hk2-utils-2.4.0-b06.jar javassist-3.18.1-GA.jar javax.annotation-api-1.2.jar javax.inject-2.4.0-b06.jar javax.servlet-api-3.0.1.jar javax.ws.rs-api-2.0.1.jar jaxb-api-2.2.7.jar jersey-client.jar jersey-common.jar jersey-container-servlet-core.jar jersey-container-servlet.jar jersey-guava-2.15.jar jersey-server.jar org.osgi.core-4.2.0.jar osgi-resource-locator-1.0.1.jar persistence-api-1.0.jar validation-api-1.1.0.Final.jar
Comments are closed for "RESTful Services HTTP basic Authentication".
Hi Joe, i really like your RESTful Services series tutorial, in this tutorial, i am using the zipped project and import it in Eclipse(Luna),however,when i tried to send request in Advanced Rest Client,i got the following error in the console
Servlet.service() for servlet [RESTful Jersey Hello World Service] in context with path [/RESTful_Service_Authentication] threw exception [Filter execution threw an exception] with root cause
java.lang.ClassNotFoundException: java.util.Base64
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1305)
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1157)
Basically, it is saying that java.lang.ClassNotFoundException: java.util.Base64, but there is not compiling error in Eclipse, i have no idea why this is happening. Can you help me please ? i am runing JDK 8 and have tested both on Tomcat 7 and Tomcat 8,producing the same error.
Thanks
Nice…
Can you tell me why Jersey’s ServletContainer is more preferred to spring’s DispatcherServlet?
@Harry,
“Edit Server Runtime Environment” and ensure that the JRE is pointing to JDK8.
Very helpful. Good explanation.
[…] In this RESTful services tutorial series, we will see about how to intercept a request in JAX-RS restful web service. JAX-RS specification is the Java API for RESTful web services and Jersey is its reference implementation. In the previous tutorial we saw about doing authentication in REST with plain servlet filters. […]
Joe,
Thanks for these tutorials. This was the simplest version of adding basic authentication to a REST service that I could find.
Great job!
Hi Joe,
I am deploying this in a Weblogic 10 container (forced to use Jersey 1.2 due to JDK 5). At any rate, when I send the basic auth header, it appears that Weblogic wants a valid weblogic user (and will not allow me to intercept the request in my filter) and fails. Any idea how i can bypass this?
Thanks!
Hi Joe,
Thanks so much for this tutorial – it actually works !!!
Very helpful and working fine :)
Thanks a lot :)
Hi, thank you for this post.
I have one doubt
After calling AuthenticationService, when it returns “true”
You have called doFilter method.
if (authenticationStatus) {
filter.doFilter(request, response);}
What does this filter.doFilter() method does excatly??
This is perfect!!
Clear and to the point.
Thanks a lot.
Cheers,
Anhad
Thanks a lot, very great article with complete information.
Thank you for the tutorial. I am able to run it actually.
Thanks a lot man ! Life saver. Great job !