How to secure Jersey REST services with Spring Security and Basic authentication

In my previous blog post, Quick way to check if the REST API is alive – GET details from Manifest file, I showed how to develop a REST resource to easy check if the developed REST API is reachable. In this post I will present how you can secure this resource with Spring Security and Basic authentication – “In the context of an HTTP transaction, basic access authentication is a method for an HTTP user agent to provide a user name and password when making a request.”

The secured REST resources introduced here are part of bigger project, presented extensively in the Tutorial – REST API design and implementation in Java with Jersey and Spring

Software used

  1. Jersey JAX-RS implementation 2.14
  2. Spring 4.1.4
  3. Spring security 3.2.5
  4. Maven 3.1.1
  5. JDK 7

Spring security configuration

Libraries

To secure the REST services with basic authentication, the following Spring security libraries are necessary in the classpath. Because I am using Maven, they are listed as Maven dependencies in the pom.xml:

<!-- Spring security -->
<dependency>
	<groupId>org.springframework.security</groupId>
	<artifactId>spring-security-core</artifactId>
	<version>${spring.security.version}</version>
</dependency>
<dependency>
	<groupId>org.springframework.security</groupId>
	<artifactId>spring-security-web</artifactId>
	<version>${spring.security.version}</version>
</dependency>
<dependency>
	<groupId>org.springframework.security</groupId>
	<artifactId>spring-security-config</artifactId>
	<version>${spring.security.version}</version>
</dependency>

Security-application context

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
    xmlns:beans="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:security="http://www.springframework.org/schema/security"
    xsi:schemaLocation="
    	http://www.springframework.org/schema/beans
    	http://www.springframework.org/schema/beans/spring-beans.xsd

        http://www.springframework.org/schema/security
        http://www.springframework.org/schema/security/spring-security.xsd">

	<!-- Stateless RESTful services use BASIC authentication -->
    <security:http create-session="stateless" pattern="/manifest/**">
        <security:intercept-url pattern="/**" access="ROLE_REST"/>
        <security:http-basic/>
    </security:http>

    <security:authentication-manager>
        <security:authentication-provider>
            <security:user-service>
                <security:user name="rest" password="rest" authorities="ROLE_REST"/>
            </security:user-service>
        </security:authentication-provider>
    </security:authentication-manager>

</beans:beans>

As you can see, a “rest” user and role is defined in memory. These are defined in the element <security:user-service> and its child element <security:user>. This makes sure that only users with ROLE_REST role are able to reach:

<security:authentication-manager>
	<security:authentication-provider>
		<security:user-service>
			<security:user name="rest" password="rest" authorities="ROLE_REST"/>
		</security:user-service>
	</security:authentication-provider>
</security:authentication-manager>

The next step is to secure the /manifest/* URLs, allowing access only to the newly defined rest user:

<security:http create-session="stateless" pattern="/manifest/**">
	<security:intercept-url pattern="/**" access="ROLE_REST"/>
	<security:http-basic/>
</security:http>

Basic HTTP authentication is enabled in our application by the <security:http-basic/> line.

Note:
You cannot define the security constraints of Spring Security in the applicationContext.xml file, because they need to be loaded up with the Servlet listeneer and the filter chain. They need to be in a proper WebApplicationContext defined with a Servlet listener, not the Servlet-related one. This is because the DelegatingFilterProxy will look for the root application context defined in the ServletContext that is loaded by the ContextLoaderListener.  If you define only applicationContext.xml, because the filters load first, before the servlets, the fiilter won’t be able to find any application context, so it won’t be able to load up correctly.

Web.xml

Extend now the contextConfigLocation context parameter, to be aware of the the new spring security configuration file security-context.xml:

<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>
		classpath:spring/applicationContext.xml
		classpath:spring/security-context.xml
	</param-value>
</context-param>

Hook in Spring security only for URLs related to the manifest:

<servlet>
	<servlet-name>jersey-servlet</servlet-name>
	<servlet-class>
		org.glassfish.jersey.servlet.ServletContainer
	</servlet-class>
	<init-param>
		<param-name>javax.ws.rs.Application</param-name>
		<param-value>org.codingpedia.demo.rest.RestDemoJaxRsApplication</param-value>
	</init-param>
	<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
	<servlet-name>jersey-servlet</servlet-name>
	<url-pattern>/*</url-pattern>
</servlet-mapping>

<!--Hook into spring security-->
<filter>
	<filter-name>springSecurityFilterChain</filter-name>
	<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

<filter-mapping>
  <filter-name>springSecurityFilterChain</filter-name>
  <url-pattern>/manifest/*</url-pattern>
</filter-mapping>

The Spring security filter chain needs to be activated.

Testing

Browser

If you access a secured location via the browser, a standard HTTP authentication popup appears asking for the authentication details:

standard-popup-browser

Put in rest/rest and you should receive the JSON response.

SoapUI

It’s fairly easy to test a secured REST with Basic Authentication via soapUI. See the following video to find out more:

Request

GET http://localhost:8888/demo-rest-jersey-spring/manifest HTTP/1.1
Accept-Encoding: gzip,deflate
Accept: application/json
Host: localhost:8888
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)
Authorization: Basic cmVzdDpyZXN0

Note the Authorization header which is constructed as follows:

  1. Username and password are combined into a string “username:password”
  2. The resulting string is then encoded using the RFC2045-MIME variant of Base64, except not limited to 76 char/line
  3. The authorization method and a space i.e. “Basic ” is then put before the encoded string.

Response

HTTP/1.1 200 OK
Date: Tue, 03 Feb 2015 15:47:32 GMT
Content-Type: application/json
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, DELETE, PUT
Access-Control-Allow-Headers: X-Requested-With, Content-Type, X-Codingpedia
Vary: Accept-Encoding
Content-Encoding: gzip
Content-Length: 196
Server: Jetty(9.2.6.v20141205)

{"Implementation-Title":"DemoRestWS","Implementation-Version":"0.0.1-SNAPSHOT","Implementation-Vendor-Id":"org.codingpedia","Built-By":"Matei1.Adrian","Build-Jdk":"1.7.0_40","Manifest-Version":"1.0","Created-By":"Apache Maven 3.1.1","Specification-Title":"DemoRestWS","Specification-Version":"0.0.1-SNAPSHOT"}

Summary

Well, that’s it. Spring Security is a very powerful framework with a gazzilion of configuration options. In this post I just shown one of them, namely how to secure REST resources with Basic Authentication. Of course in a more realistic scenario you would store the users and roles in an LDAP directory, or database…

Note:
If you decide to use Basic Authentication to secure your REST resources, please make sure they are called over HTTPS. The preferred way nowadays to secure REST resources is with OAuth. More on that on a later post.

Resources

Podcastpedia image

Adrian Matei

Creator of Podcastpedia.org and Codingpedia.org, computer science engineer, husband, father, curious and passionate about science, computers, software, education, economics, social equity, philosophy - but these are just outside labels and not that important, deep inside we are all just consciousness, right?

Free Programming Books are now on Codingmarks.org

We're happy to announce that we've reached and surpassed our goal of 1 Mb #codingmarks, by importing the books from the Free-Programming-Books project Continue reading