Securing a REST Service with Spring Security 3.1

Last Update: 12.04.2013 (3rd update)

Table of Contents

1. Overview

This is the third of a series of articles about setting up a secure REST Service using Spring 3.1 and Spring Security 3.1 with Java based configuration. This article will focus on the security configuration using Spring Security 3.1, assuming some understanding of Spring Security basics and focusing on the specifics of securing the RESTful web service.

The REST with Spring series:

2. Introducing Spring Security in the web.xml

The architecture of Spring Security is based entirely on Servlet Filters and, as such, comes before Spring MVC in regards to the processing of HTTP requests. Keeping this in mind, to begin with, a filter needs to be declared in the web.xml of the application:

<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>/*</url-pattern>
</filter-mapping>

The filter must necessarily be named ‘springSecurityFilterChain’  to match the default bean created by Spring Security in the container.

Note that the defined filter is not the actual class implementing the security logic but a DelegatingFilterProxy with the purpose of delegating the Filter’s methods to an internal bean. This is done so that the target bean can still benefit from the Spring context lifecycle and flexibility.

The URL pattern used to configure the Filter is /* even though the entire web service is mapped to /api/* so that the security configuration has the option to secure other possible mappings as well, if required.

3. The security configuration

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

   <http entry-point-ref="restAuthenticationEntryPoint">
      <intercept-url pattern="/api/admin/**" access="ROLE_ADMIN"/>

      <custom-filter ref="myFilter" position="FORM_LOGIN_FILTER"/>

      <logout />
   </http>

   <beans:bean id="myFilter" class=
    "org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
      <beans:property name="authenticationManager" ref="authenticationManager"/>
      <beans:property name="authenticationSuccessHandler" ref="mySuccessHandler"/>
   </beans:bean>
   <beans:bean id="mySuccessHandler"
    class="org.rest.security.MySavedRequestAwareAuthenticationSuccessHandler"/>

   <authentication-manager alias="authenticationManager">
      <authentication-provider>
         <user-service>
            <user name="temporary" password="temporary" authorities="ROLE_ADMIN"/>
            <user name="user" password="user" authorities="ROLE_USER"/>
         </user-service>
      </authentication-provider>
   </authentication-manager>

</beans:beans>

Most of the configuration is done using the security namespace – for this to be enabled, the schema locations must be defined and pointed to the new 3.1 versions. The namespace is designed so that it expresses the common uses of Spring Security while still providing hooks to the underlying beans.

3.1. The basics

The <http> element is the main container element for HTTP security configuration. In the current implementation, it only secured a single mapping: /api/admin/**. Note that the mapping is relative to the root context of the web application, not to the rest servlet; this is because the entire security configuration lives in the root Spring context and not in the child context of the Servlet.

3.2. The entry point

In a standard web application, the authentication process may be automatically triggered when the client tries to access a secured resource without being authenticated – this is usually done by redirecting to a login page so that the user can enter credentials. However, for a RESTful Web Service this behavior doesn’t make much sense – authentication should only be done by a request to the correct URI and all other requests should simply fail with a 401 UNAUTHORIZED status code if the user is not authenticated.

Spring Security handles this automatic triggering of the authentication process with the concept of an entry point; the entry point is a required part of the configuration, and can be injected via the entry-point-ref attribute of the <http> element. Keeping in mind that this functionality doesn’t make sense in the context of the RESTful web service, the new custom entry point is defined:

@Component( "restAuthenticationEntryPoint" )
public final class RestAuthenticationEntryPoint implements AuthenticationEntryPoint{

   @Override
   public void commence( HttpServletRequest request, HttpServletResponse response, 
    AuthenticationException authException ) throws IOException{
      response.sendError( HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized" );
   }
}

3.3. The login

There are multiple ways to do authentication for a RESTful Web Service – one of the default Spring Security provides is form login – which uses an authentication processing filter – org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.

Since the <http> element doesn’t automatically create this particular filter by default, it needs to be explicitly specified in the configuration, using the <custom-filter> element at the position FORM_LOGIN_FILTER; the only required dependency for this bean is the authentication manager.

Note that for a standard web application, the auto-configattribute of the <http> element is shorthand syntax for some useful security configuration. While this may be appropriate for some very simple configurations, it doesn’t fit and should not be used for a REST API.

3.4. Authentication should return 200 instead of 301

By default, form login will answer a successful authentication request with a 301 MOVED PERMANENTLY status code; this makes sense in the context of an actual login form which needs to redirect after login. For a RESTful web service however, the desired response for a successful authentication should be 200 OK.

This is done by injecting a custom authentication success handler in the form login filter, to replace the default one. The new handler implements the exact same login as the default org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler with one notable difference – the redirect logic is removed:

// Use the DefaultSavedRequest URL
// final String targetUrl = savedRequest.getRedirectUrl();
// this.logger.debug( "Redirecting to DefaultSavedRequest Url: " + targetUrl );
// this.getRedirectStrategy().sendRedirect( request, response, targetUrl );

3.5. The authentication manager and provider

The authentication process uses an in-memory provider to perform authentication – this is meant to simplify the configuration as a production implementation of these artifacts is outside the scope of this post.

4. Maven and other trouble

In addition to the pom.xml from the first post, as well as the one from the second post, the Spring Security maven dependencies need to be added:

<dependencies>
   <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>
   <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>${spring.version}</version>
   </dependency>
   <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>${spring.version}</version>
   </dependency>
</dependencies>

<properties>
   <spring-security.version>3.1.3.RELEASE</spring-security.version>
   <spring.version>3.2.2.RELEASE</spring.version>	
</properties>

Notice that the security artifacts define dependencies to the 3.0.x versions of Spring, more specifically spring-security-web depends on spring-aop and on spring-tx version 3.0.x instead of the expected 3.1.x.

To understand why this is a problem, we need to understand how the Maven conflict resolution algorithm works – in case of conflict, Maven will chose which jar to include based on the distance between the particular dependency and the root of the tree. In our case, the conflicts are the spring-aop and spring-tx jars, appearing once with version 3.0.6 and once with 3.1.0. In the case of spring-aop, it appears once as a level 1 dependency of both spring-security-web and spring-security-config with version 3.0.6 , and once as a level 2 dependency of spring-webmvc with the version 3.1.0; since the 3.0.6 versioned jar is closer to the root, it will be the one chosen by the conflict resolution mechanism.

Now that we understand why it is that Maven will deploy the 3.0.6 version of the jars with the application and not the intended 3.1.0 version, we need to address the issue. The solution is to add the two dependencies, with the intended 3.1.0 versions, directly into the pom – this will shorten the distance between them and the root to 0 and will force Maven to use them first.

5. Conclusion

This post covered the basic security configuration and implementation for a RESTful service using Spring Security 3.1, discussing the web.xml, the security configuration, the HTTP status codes for the authentication process and the Maven resolution of the security artifacts. In the next articles I will focus on a Java based configuration for Spring Security, integration testing of the secure API using the rest-assured library and HTTP basic authentication. In the meantime, check out the github project.


P.S. If you read this far, you should follow me on Twitter.

, , ,

  • http://www.toptreadmillsforhome.com/ top 10 treadmills for home

    Great information, thanks for the share!

  • Ben

    The issue for me is having users inside my xml. How would you redirect the user auth to an external resource. Would you overwrite authenticationManager?

  • Eugen

    As it is mentioned in the article, the in memory authentication provider is only used because using a real provider is outside the scope of this post. Thanks for the feedback.

  • Alwold

    On the iPhone, your “follow me” button overlaps the text, so it’s hard to read. You may want to take a look… Nice article though.

  • Sigmund Lundgren

    Hmm successful auth still redirects for my, done in the spring base class. Had to comment this line in the successhandler:

    //super.onAuthenticationSuccess(request, response, authentication);

    • René Fleischhauer

      Hi Sigmund, all,

      I have the same problem…

      super.onAuthenticationSuccess(request, response, authentication) calls SimpleUrlAuthenticationSuccessHandler#handle which contains the following line

      redirectStrategy.sendRedirect(request, response, targetUrl);

      Seems to be pretty non-sense to call the super method and therefore, I also removed it. Additionally I added a response.setStatus(200) for safety purposes…

      Best,
      René

  • fadi

    Hi,

    Am having a problem in understanding one thing, how the actual login will be done. I added the configuration and when trying to access a rest method/url I get 401 error, but am unable to figure out how and where to login. can help me pleas?

    • Eugen

      Because it’s REST, there is should be no state on the server side. What that means is that the concept of login doesn’t fit the RESTful architecture – each request will contain the credentials for it to be authenticated. So, just add the correct Authentication headers and you’ll be OK. Also, if you’re looking at the github project, there is extensive testing that does that – you can take a look at that.
      Thanks.
      Eugen.

      • fadi

        Thank you for the response, but am using FF plugin and I added the Authorization header to the url, the only place fired in my code is the EntryPoint and it never enters my AuthenticationProvider. which makes wounder if am doing something wrong!

        Thanks

        • baeldung

          Not sure how you can add the header to the URL – do you mean you added the header to the request? Also, to follow some working examples, you can always clone the project from github and run the tests.

      • DropDeadFred

        If you are using the REST service through AJAX from a browser can you had the necessary Authentication headers? For example, if you are using form based authentication to your website and want to retrieve data from your REST service. I guess I must just be missing something here.

      • Branislav Vidovic

        This is the most interesting part in my opinion…. I am interested which other additional component of spring security you have extended to read request headers and build a required authentication object. Besides i had a look into your git project but in there you did not do it totally like in this post..

  • NiceFred

    Thank you for a good article about this, just what I needed!

  • Naama

    Great article!

  • Neomi

    Great article!
    But what is the login uri? Where is it defined?

  • http://profile.yahoo.com/DULVM64SMPX6Q74MRXYD4TFHPA Marc de Verdelhan

    Your article was just what I needed. But I tested your solution and it always returned a “401 Unauthorized”.
    Finally I used the following config:

    Now it’s simpler and it works fine. Could you explain why?

    Thank you.

  • René Fleischhauer

    Hi all,

    thanks for the tutorial. Great stuff! I have a minor question:

    I added within the element in order to customize the login url. After starting the application again the following exception occurs:

    org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Filter beans ” and ” have the same ‘order’ value. When using custom filters, please make sure the positions do not conflict with default filters. Alternatively you can disable the default filters by removing the corresponding child elements from and avoiding the use of .

    The problem is clear, however I’m not sure how I can avoid the use of j_spring_security_check. I’d highly appreciate any hint.

    Best,
    René

  • Thedug

    I was just checking out the github project. The security xml file is quite different from what I see here. There’s no mention of the success handler or the entry point. Is that intentional?

    It also introduces a digest authentication and references a BASIC_AUTH_FILTER, but I haven’t seen where that is defined.

    • AlexCzar

      I have the same question, where do I define injection of MySavedRequestAwareAuthenticationSuccessHandler?

  • Pingback: How to make Spring-based app work with jsessionid URL parameter | PHP Developer Resource

  • glz

    Great article, however every time I get 401 Unauthorized, regardless sending or not valid username and password (temporary/temporary) in username or j_username and password or j_password. Tried to figure it out looking at your git project, but there seems to be a lot of other stuff, and the only authentication used in this project is Digest authentication. Did I miss something important here?

  • bhecht

    This was very helpful.

    I as well got an 401 Unauthorized.
    After dubugging i found out the problem is at UsernamePasswordAuthenticationFilter.requiresAuthentication() which returns true only if the URI ends with /j_spring_security_check.
    I had to override this class and return true in the requiresAuthentication(), so it will work.

Powered by WordPress. Designed by Woo Themes