Monday, January 11, 2016

Configuring Spring Security for a RESTful Web Services

Ref:- http://www.networkedassets.com/en/configuring-spring-security-for-a-restful-web-services/

Introduction

Recently, I have spent some time searching for the solution of the following problem: how to secure a RESTful service with a Spring Security framework without using the login form? I haven’t found a satisfactory answer for my question. After a couple of hours I have implemented the solution successfully, I searched through all the tips and suggestions in tutorials and forums. I hope that this solution will be helpful to someone and will save much valuable time. So let’s get to work…
Problem description
We have a web application that uses Spring Security and we want to have a RESTful service that has access to components secured with Spring Security. In the standard scenario of a web application, the authentication process is triggered automatically when the client tries to get access to a secured resource without being authenticated – usually by redirecting to a login page with a form for credentials. For a RESTful Web Service it doesn’t make much sense since the RESTful services are not going to be accessed by a web browser – authentication should be done only by a request to correct URI and all other incorrect requests (if a user is not authenticated) should fail with an appropriate status code.
So we have a few clever requirements and restrictions:
  • refer to the RESTful service directly by URL without using login form,
  • return 401 UNAUTHORIZED status code if the user is not authenticated,
  • don’t create Spring Security session, authentication should be performed ‘only for request time’,
  • if there are other resources secured by Spring Security with using a login form and authentication session, RESTful service authentication has to be independent (so we cannot affect on existing Spring Security session),
  • we do not have to bother us with issues in regard to spying (e.g. wiretapping) since the application is used in the Intranet,
  • it is possible to access secured components from the aforementioned RESTful service.
The UML sequence diagram below describes how the solution should work:
Solution
Note: The following solution requires Spring Security in version minimum 3.1.
Let’s suppose that service is accessible by URL: http://server:port/rest/ProductsService/getProductsList . For the authentication we can simply use URL query parameters. So the request will look like the following: http://server:port/rest/ProductsService/getProductsList/?j_username=normal_user&j_password=password. j_usernameand j_password are authentication credentials (names of these parameters are default for Spring Security for example in login form).
REST Service code might look like below (Jersey REST implementation has been used).
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
package org.example.rest;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
@Path("/ProductsService")
@Service
@Scope("request")
public class ProductsService {
@Autowired
private ProductsController products;
@POST
@Path("/getProductsList")
@Produces("text/plain")
public String getProductsList() {
return products.getProductsList();
}
}
And here is the code of a secured component which is used in the service.
01
02
03
04
05
06
07
08
09
10
11
package org.example.rest;
import org.springframework.security.access.annotation.Secured;
import org.springframework.stereotype.Component;
@Component
@Secured("ROLE_ADMIN")
public class ProductsController {
public String getProductsList() {
// Implementation of method
return "";
}
}
The solution consists of two types of actions: XML file configuration and Java class implementation. To make the implementation, the following steps have to be performed:
1. Configuring Spring beans
Spring beans XML configuration file should have elements like in the following example:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<context:annotation-config />
<context:component-scan base-package="org.example" />
<!-- Security configuration for REST services. -->
<security:http pattern="/rest/ProductsService/getProductsList/**"
authentication-manager-ref="authenticationManager" entry-point-ref="restServicesEntryPoint"
create-session="stateless">
<security:custom-filter ref="restServicesFilter"
before="PRE_AUTH_FILTER" />
</security:http>
<!-- Entry point for REST service. -->
<bean id="restServicesEntryPoint"
class="org.example.security.RestAuthenticationEntryPoint" />
<!-- Filter for REST services. -->
<bean id="restServicesFilter"
class="org.example.security.RestUsernamePasswordAuthenticationFilter">
<property name="postOnly" value="false" />
<property name="authenticationManager" ref="authenticationManager" />
<property name="authenticationSuccessHandler" ref="restServicesSuccessHandler" />
</bean>
<!-- A handler for successful REST service authentication. -->
<bean id="restServicesSuccessHandler"
class="org.example.security.RestAuthenticationSuccessHandler" />
<!-- Security configuration for other resources. Use login form. -->
<security:http use-expressions="true" create-session="always">
<security:intercept-url pattern="/**"
access="isAuthenticated()" />
<security:form-login login-page="/security/login.html?project=sample-project"
authentication-failure-url="/security/login.html?project=sample-project&amp;error=true" />
<security:logout logout-success-url="/security/login.html?project=sample-project" />
</security:http>
<!-- Authentication manager. -->
<authentication-manager alias="authenticationManager">
<authentication-provider>
<user-service>
<user name="admin_user" password="password" authorities="ROLE_ADMIN" />
<user name="normal_user" password="password" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
<security:global-method-security
secured-annotations="enabled" jsr250-annotations="enabled"
pre-post-annotations="enabled">
</security:global-method-security>
In the example above a few crucial XML elements are defined:
  • - encapsulates all HTTP specific configuration. The pattern attribute is used to define a control access rule for the URL pattern /rest/ProductsService/getProductsList/*, which conveniently matches the REST service resource that we want to secure. authentication-manager-ref=”authenticationManager”attribute – points on defined authentication manager bean. This element has also entry-point-ref attribute (entry point is a concept of Spring Security for handling unauthenticated access, for example by redirecting to login page) which refers to further defined bean. You should also pay attention to the sub element which refers to filter bean to be applied.
  • element refers to entry point class which implementation has been shown in the step 2.
  • is also a bean of class which has to be implemented. That element has one interesting sub element,
  • . It is a handler for successful authentication event (step 4.)
  • Note that in described XML file there is another element defined, which determines the authentication for other resources by login form. It means, that it is possible to mix different rules of the authentication for different points (resources) in our application. That differs Spring Security 3.1 from older versions, which allows defining only one element in that XML. Without that, the feature mixing different authentication resources is difficult to achieve (I don’t even know if it is possible).
2. Implementation of an entry point
As it was mentioned, a class for the entry point has to be implemented. It should look like in the example below.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
package org.example.security;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
/**
* Authentication entry point for REST services
*/
public final class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
}
}
As you can see, implementation of entry point is quite simple.
Class extends org.springframework.security.web.AuthenticationEntryPoint, and implements only one method, which sends response error (with 401 status code) in cause of unauthorized attempt.
3. Implementation of an authentication filter
Required implementation of the authentication filter is a little bit more complicated. Class must extend org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package org.example.security;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
/**
* Authentication filter for REST services
*/
public class RestUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
@Override
protected boolean requiresAuthentication(HttpServletRequest request,
HttpServletResponse response) {
boolean retVal = false;
String username = request.getParameter("j_username");
String password = request.getParameter("j_password");
if (username != null && password != null) {
Authentication authResult = null;
try {
authResult = attemptAuthentication(request, response);
if (authResult == null) {
retVal = false;
}
} catch (AuthenticationException failed) {
try {
unsuccessfulAuthentication(request, response, failed);
} catch (IOException e) {
retVal = false;
} catch (ServletException e) {
retVal = false;
}
retVal = false;
}
try {
successfulAuthentication(request, response, authResult);
} catch (IOException e) {
retVal = false;
} catch (ServletException e) {
retVal = false;
}
return false;
} else {
retVal = true;
}
return retVal;
}
}
In the presented class a requiresAuthentication(HttpServletRequest request,HttpServletResponse response) method is overriden, which simply returns true or false depending on read credentials. Username and password are taken from HttpServletRequest as parameters. Authentication is performed by attemptAuthentication(request, response)and depending on result of it a successfullAuthentication or unsuccessfulAuthentication method is called, whichdelegates to appropriate authentication handlers.
4. Implementation of a successful authentication handler
Implementations of the successful authentication handler can do almost everything, for example control the navigation to the subsequent destination (redirect or forward). In cause of RESTful Web Service we don’t want to do anything after logging in, but simply return a service response. Handler class extends AuthenticationSuccessHandler interface, by empty onAuthenticationSuccess method implementation.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
package org.example.server.security;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
/**
* Authentication success handler for REST services
*
*/
public class RestAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
}
And that’s it, after all steps have been taken and completed successfully, our RESTful Web Service will be secured by Spring Security framework. Now you can refer directly to the URL of the service and it will return a correct response when the successful authentication with using given username and password has been done. If a username or a password is incorrect or empty, 401 status code will be returned.
Summary
The presented tutorial is intended to secure a RESTful Web Services by specifying a username and a password directly in requested URL. This solution is especially useful, when you have an application with REST services and other resources that you want to secure independently by Spring Security. As it is shown, in Spring Security 3.1 version (and next) steps to configure are quite simple to carry out, and probably meet all your needs.