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
- Jersey JAX-RS implementation 2.14
- Spring 4.1.4
- Spring security 3.2.5
- Maven 3.1.1
- 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 libraries
<!-- 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
Spring security configuration
<?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:
authenticaion manager with in memory setup
<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:
Securing URLs with role-based access
<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
:
web.xml – context-param extension
<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:
Hook into Spring security
<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:
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:
https://www.youtube.com/watch?feature=player_embedded&v=iaDU45xuyFs
Request
Request resource with Basic authentication
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:
- Username and password are combined into a string “username:password”
- The resulting string is then encoded using the RFC2045-MIME variant of Base64, except not limited to 76 char/line
- The authorization method and a space i.e. “Basic ” is then put before the encoded string.
Response
Response – manifest details
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.
Reference: | How to secure Jersey REST services with Spring Security and Basic authentication from our JCG partner Adrian Matei at the Codingpedia.org blog. |