Using Spring Security in the RESTful API

introduce

In this article, we'll learn how to use Spring and Spring Security 5 to provide a more secure RESTful API.
We will use the Java configuration to set security and authenticate using the login and Cookie methods.

Enable Spring Security

The architecture of Spring Security is entirely based on Servlet filters.
The easiest option to register a Spring Security filter is to add the @EnableWebSecurity annotation to our config class:

@Config
@EnableWebSecurity
public class SecurityJavaConfig extends WebSecurityConfigurerAdapter {
 
    // ...
}

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

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

Or we can declare it in the application's web.xml:

<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 s created by Spring Security in the container.
Note that the filters defined are not actual classes that implement security logic.Instead, it is a delegate filterproxy that delegates the method of the filter to an internal bean.This is done so that the target beans can still benefit from the lifecycle and flexibility of the Spring context.

Spring Security Java Configuration

We can configure security entirely in Java classes by creating a configuration class that extends the WebSecurityConfigurerAdapter and annotates it with @EnableWebSecurity:

@Configuration
@EnableWebSecurity
public class SecurityJavaConfig extends WebSecurityConfigurerAdapter {
 
    // ...
}

Now let's create users with different roles in SecurityJavaConfig that we will use to validate our API endpoints:

@Override
protected void configure(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 BCryptPasswordEncoder();
}

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();
}

Http

In our code implementation, we use antMatchers to create secure mappings/api/foos and/api/admin/**.
Any authenticated user can access/api/foos.On the other hand, /api/admin/** can only be accessed by admin role users.
In standard web applications, authentication may trigger automatically when unauthenticated clients attempt to access protected resources.This process is usually redirected to the login page so that users can enter credentials.
However, this behavior does not make much sense for REST Web services.We should be able to authenticate only requests with the correct URI, and if the user is not authenticated, all requests should fail with a 401 unauthorized status code.
Spring Security uses the concept of Entry Point to handle automatic triggering of the authentication process - a necessary part of the configuration that can be injected through the authenticationEntryPoint method.
Simply return 401 when triggered:

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

Login form for REST

There are several ways to authenticate the REST API.One of the defaults provided by Spring Security is form login, which uses the authentication processing filter org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.
The formLogin element creates this filter and provides additional methods, successHandler and failureHandler, to set up our custom authentication success and failure handlers, respectively.

Authentication should return 200 instead of 301

By default, form logins use a 301 status code to answer successful authentication requests, which makes sense in the context of the actual login form that needs to be redirected after login.
However, for RESTful web services, the response required for successful validation should be 200 OK.
To do this, we inject a custom authentication success handler into the form login filter to replace the default handler.The new handler implements the exact same login as the default org.springframe.security.web.authentication.SavedRequestAwareAuthentication SuccessHandler has one obvious difference - it removes the redirection 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;
    }
}

Authentication Manager and Provider

The authentication process uses an in-memory provider to perform authentication.
We created two users, a user with the USER role and an admin with the ADMIN role.

Finally - Authenticate against a running REST service

Now let's see how different users can authenticate against the REST API.
The URL for login is / login, a simple curl command that performs login for a user named user and password userPass.

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

This request will return a Cookie that we can use for any subsequent requests to the REST service.
We can use curl to authenticate and save the cookie s 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

We can then use the cookie ** in the ** file to make further authentication 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 get 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":"qulingfeng"}]

Similarly, for admin users, we can use curl to authenticate:

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

Then update the cookies access management endpoint/api/admin/*:

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

All responses succeeded because the administrative user had access to the endpoint/api/admin/*:

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

XML Configuration

We can also use XML instead of Java configuration to complete all of the above security configurations:

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

Concluding remarks

In this tutorial, we describe the basic security configuration and implementation of RESTful services using Spring security 5.
We learned how to configure security entirely through Java configuration for the REST API and studied its alternative web.xml configuration.
Next, we discussed how to create users and roles for secured applications and how to map these users to specific endpoints of the application.
Finally, we examined how to create custom authentication entry points and custom success handlers to provide greater flexibility in controlling security for applications.

Welcome to my public number: Qu Lingfeng, get exclusive learning resources and daily dry goods delivery.
If you are interested in my topic, you can also follow my blog: sagowiec.com

Tags: Programming Spring REST curl Java

Posted on Fri, 08 Nov 2019 23:01:42 -0500 by jikishlove