I just announced the newSpring Security 5 modules (primarily focused on OAuth2) in the course:

>> CHECK OUT LEARN SPRING SECURITY

1. Overview

In this tutorial, we learn how to Secure a REST API using Spring and Spring Security 5.

We will set up the security using Java configuration and will be using a Login and Cookie approach for authentication.

Further reading:

Spring Security Authentication Tutorial

How to build a production-grade Registration process for new users, and Login flow for existing users.

Read more

Granted Authority Versus Role in Spring Security

A quick guide to the difference between a granted authority and a role in Spring Security.

Read more

Spring Security Basic Authentication

Set up Basic Authentication in Spring - the XML Configuration, the Error Messages, and example of consuming the secured URLs with curl.

Read more

2. Enable Spring Security

The architecture of Spring Security is based entirely on Servlet Filters. Therefore, this comes before Spring MVC in regards to the processing of HTTP requests.

The simplest option to register the Spring security filter is by annotating our config class with @EnableWebSecurity:

@Config
@EnableWebSecurity
public class SecurityJavaConfig extends WebSecurityConfigurerAdapter {

	// ...
}

For a non-Spring Boot application, we can extend the AbstractSecurityWebApplicationInitializer and pass our config class in its constructor:

public class SecurityWebApplicationInitializer 
  extends AbstractSecurityWebApplicationInitializer {

    public SecurityWebApplicationInitializer() {
        super(SecurityJavaConfig.class);
    }
}

Or else we can declare it in 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>

We should name the filter ‘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. Instead, it is a DelegatingFilterProxy that delegate 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/*. This gives the security configuration an option to secure other possible mappings as well if required.

3. Spring Security Java Configuration

We can do Security configurations entirely in a Java class by creating a config class that extends WebSecurityConfigurerAdapter and annotating it with @EnableWebSecurity:

@Configuration
@EnableWebSecurity
public class SecurityJavaConfig extends WebSecurityConfigurerAdapter {

	// ...
}

Now, let’s create users with different roles in SecurityJavaConfig that we will be using to authenticate our API endpoints:

@Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication()
        .withUser("admin").password(encoder().encode("adminPass")).roles("ADMIN")
        .and()
        .withUser("user").password(encoder().encode("userPass")).roles("USER");
}

@Bean
public PasswordEncoder  encoder() {
    return new BCryptPassowordEncoder();
}

Next, let’s configure security for our API endpoints:

@Override
protected void configure(HttpSecurity http) throws Exception { 
    http
    .csrf().disable()
    .exceptionHandling()
    .authenticationEntryPoint(restAuthenticationEntryPoint)
    .and()
    .authorizeRequests()
    .antMatchers("/api/foos").authenticated()
    .antMatchers("/api/admin/**").hasRole("ADMIN")
    .and()
    .formLogin()
    .successHandler(mySuccessHandler)
    .failureHandler(myFailureHandler)
    .and()
    .logout();
}

>> Signup for my Course on Building a REST API with Spring 4

3.1. The http Element

The http element is the starting element for security configuration and provides us with fluent and flexible methods to configure security.

In our implementation, we are creating secured mappings /api/foos and /api/admin/** using antMatchers. 

The /api/foos pattern is accessible to any authenticated user. On the other hand, /api/admin/** will only be accessible to ADMIN role users.

3.2. The Entry Point

In a standard web application, the authentication process may automatically trigger when an un-authenticated client tries to access a secured resource. This process usually redirects to a login page so that the user can enter credentials.

However, for a REST Web Service,this behaviour doesn’t make much sense. We should be able to authenticate only by a request to the correct URI and if the user is not authenticated all requests should simply fail with a 401 UNAUTHORIZED status code.

Spring Security handles this automatic triggering of the authentication process with the concept of an Entry Point – this is a required part of the configuration, and can be injected via the authenticationEntryPoint method.

Keeping in mind that this functionality doesn’t make sense in the context of the REST Service, we define the new custom entry point to simply return 401 when triggered:

@Component
public final class RestAuthenticationEntryPoint 
  implements AuthenticationEntryPoint {

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

A quick side note here is that the 401 is sent without the WWW-Authenticate header, as required by the HTTP Spec. We can, of course, set the value manually if we need to.

3.3. The Login Form for REST

There are multiple ways to do Authentication for a REST API. One of the defaults Spring Security provides is Form Login – which uses an authentication processing filter – org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.

The formLogin element will create this filter and also provides additional methods successHandler and failureHandler to set our custom authentication success and failure handlers respectively.

Note that for a standard web application, the @EnableWebSecurity annotation configures a lot of default configurations itself.

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.

However, for a RESTful web service, the desired response for a successful authentication should be 200 OK.

We do this 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 – it removes the redirect logic:

public class MySavedRequestAwareAuthenticationSuccessHandler 
  extends SimpleUrlAuthenticationSuccessHandler {

    private RequestCache requestCache = new HttpSessionRequestCache();

    @Override
    public void onAuthenticationSuccess(
      HttpServletRequest request,
      HttpServletResponse response, 
      Authentication authentication) 
      throws ServletException, IOException {
 
        SavedRequest savedRequest
          = requestCache.getRequest(request, response);

        if (savedRequest == null) {
            clearAuthenticationAttributes(request);
            return;
        }
        String targetUrlParam = getTargetUrlParameter();
        if (isAlwaysUseDefaultTargetUrl()
          || (targetUrlParam != null
          && StringUtils.hasText(request.getParameter(targetUrlParam)))) {
            requestCache.removeRequest(request, response);
            clearAuthenticationAttributes(request);
            return;
        }

        clearAuthenticationAttributes(request);
    }

    public void setRequestCache(RequestCache requestCache) {
        this.requestCache = requestCache;
    }
}

3.5. Failed Authentication should return 401 instead of 302

Similarly – we configured the authentication failure handler – the same way we did with the success handler.

Luckily – in this case, we don’t need to actually define a new class for this handler – the standard implementation – SimpleUrlAuthenticationFailureHandler – does just fine.

3.6. 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 article.

We have created two users namely user having role USER and admin with role ADMIN.

3.7. Finally – Authentication against the running REST Service

Now let’s see how we can authenticate against the REST API for different users.

The URL for login is /login – and a simple curl command performing login for the user of name user and password userPass would be:

curl -i -X POST -d username=user -d password=userPass
http://localhost:8080/spring-security-rest/login

This request will return the Cookie which we can use for any subsequent request against the REST Service.

We can use curl to authentication and store the cookie it receives in a file:

curl -i -X POST -d username=user -d password=userPass -c /opt/cookies.txt 
http://localhost:8080/spring-security-rest/login

Then we can use the cookie from the file to do further authenticated requests:

curl -i --header "Accept:application/json" -X GET -b /opt/cookies.txt 
http://localhost:8080/spring-security-rest/api/foos

Since the user can access the /api/foos/* endpoint, this authenticated request will correctly result in a 200 OK:

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Wed, 24 Jul 2013 20:31:13 GMT

[{"id":0,"name":"JbidXc"}]

Similarly, for admin user we can use curl for authentication:

curl -i -X POST -d username=admin -d password=adminPass -c /opt/cookies.txt 
http://localhost:8080/spring-security-rest/login

and then updated cookies for accessing admin endpoints /api/admin/*:

curl -i --header "Accept:application/json" -X GET -b /opt/cookies.txt 
http://localhost:8080/spring-security-rest/api/admin/x

Since admin user can access endpoint /api/admin/* this will result in success response:

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Type: application/json;charset=ISO-8859-1
Content-Length: 5
Date: Mon, 15 Oct 2018 17:16:39 GMT

Hello

4. The XML Security Configuration

We can also do all the above security configuration using XML instead of Java configuration:

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

    <form-login 
      authentication-success-handler-ref="mySuccessHandler" 
      authentication-failure-handler-ref="myFailureHandler" />

    <logout />
</http>

<beans:bean id="mySuccessHandler"
  class="org.rest.security.MySavedRequestAwareAuthenticationSuccessHandler"/>
<beans:bean id="myFailureHandler" class=
  "org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler"/>

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

Most of the configuration is done using the security namespace. To enable this, we need to define the schema locations.

The namespace is designed so that it expresses the common use cases of Spring Security while still providing hooks for raw beans to accommodate more advanced scenarios.

5. Conclusion

In this tutorial, we covered the basic security configuration and implementation for a RESTful Service using Spring Security 5.

We learned how to do Security configuration for our REST API entirely via Java config and also looked at its web.xml configuration alternative.

Next, we discussed how to create users and roles for our secured application and also how to map these users to specific endpoints of our application.

Finally, we also looked into creating custom Authentication Entry Point and a custom Success Handler that gave our application a better flexibility in terms of controlling security.

The full implementation is available over on Github.

I just announced the new Spring Security 5 modules (primarily focused on OAuth2) in the course:

>> CHECK OUT LEARN SPRING SECURITY

newest oldest most voted
Notify of
Sigmund Lundgren
Guest
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
Guest
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
Guest
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 Paraschiv
Guest

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
Guest
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

Eugen Paraschiv
Guest

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
Guest
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.

Eugen Paraschiv
Guest

You can take a look at: http://code.google.com/p/crypto-js/

Branislav Vidovic
Guest
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..

Eugen Paraschiv
Guest

The article is now updated with the exact process of how to perform the login and how to use the cookie in further requests.

NiceFred
Guest
NiceFred

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

René Fleischhauer
Guest
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… Read more »

Eugen Paraschiv
Guest

Hi – yes, using the element is a good alternative as it does keep the configuration simple – please check out the updated configuration section and the new github project for a working implementation. Thanks.

glz
Guest
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?

Eugen Paraschiv
Guest

Hi – I updated the article to better explain how to perform login and how to interact with the service; I also added a specific github project to only cover this article – should be simpler to understand. Thanks.