Securing Spring Boot with In-Memory basic authentication

In this tutorial we will learn how to enable in-memory basic authentication for a simple REST Service using Spring Boot. We will configure two different users with different Roles and add a Test class to verify the in-memory basic authentication.

Create a new Spring Boot project

Start by creating a basic Spring Boot project which includes the following dependencies:

<?xml version="1.0" encoding="UTF-8"?><project>
   	
   <parent>
       		
      <groupId>org.springframework.boot</groupId>
       		
      <artifactId>spring-boot-starter-parent</artifactId>
       		
      <version>2.1.9.RELEASE</version>
       		
      <relativePath/>
         	
   </parent>
    	
   <groupId>com.example</groupId>
    	
   <artifactId>demo-rest-secured</artifactId>
    	
   <version>0.0.1-SNAPSHOT</version>
    	
   <name>demo</name>
    	
   <description>Demo project for Spring Boot</description>
     	
   <properties>
       		
      <java.version>1.8</java.version>
       	
   </properties>
     	
   <dependencies>
       		
      <dependency>
          			
         <groupId>org.springframework.boot</groupId>
          			
         <artifactId>spring-boot-starter-web</artifactId>
          		
      </dependency>
        		
      <dependency>
          			
         <groupId>org.springframework.boot</groupId>
          			
         <artifactId>spring-boot-starter-test</artifactId>
          			
         <scope>test</scope>
          		
      </dependency>
       		
      <!-- spring security -->
       		
      <dependency>
          			
         <groupId>org.springframework.boot</groupId>
          			
         <artifactId>spring-boot-starter-security</artifactId>
          		
      </dependency>
        		
      <!-- spring security test -->
       		
      <dependency>
          			
         <groupId>org.springframework.security</groupId>
          			
         <artifactId>spring-security-test</artifactId>
          			
         <scope>test</scope>
          		
      </dependency>
        	
   </dependencies>
    
</project>

The Spring Security Configuration is defined in a @Configuration Bean which extends WebSecurityConfigurerAdapter:

package com.example.testrest;

import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication()
        .withUser("user")
        .password("{noop}password")
        .roles("USER")
        .and()
        .withUser("admin")
        .password("{noop}password")
        .roles("USER", "ADMIN");
  }

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.httpBasic()
        .and()
        .authorizeRequests()
        .antMatchers(HttpMethod.GET, "/**")
        .hasRole("USER")
        .antMatchers(HttpMethod.POST, "/")
        .hasRole("ADMIN")
        .and()
        .csrf()
        .disable()
        .formLogin()
        .disable();
  }
}

As you can see, we have added two Roles:

  • user which belongs to the USER Roles
  • admin which belongs to the USER,ADMIN Roles

Also, note that in the above example each password has a prefix of {x} which specifies what password encoder should be used to encode the provided password. Below are different values you can use as password prefix:

  • Use {bcrypt} for BCryptPasswordEncoder,
  • Use {noop} for NoOpPasswordEncoder,
  • Use {pbkdf2} for Pbkdf2PasswordEncoder,
  • Use {scrypt} for SCryptPasswordEncoder,
  • Use {sha256} for StandardPasswordEncoder.

Therefore, in order to use HTTP POST methods you the “ADMIN” Role, while the HTTP GET can be executed by the “USER” Role. Let’s add a Controller class with a GET and POST method in it:

package com.example.testrest;

import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
public class CustomerController {
  @Autowired CustomerRepository repository;

  @RequestMapping(
      method = RequestMethod.GET,
      produces = {"application/json"})
  public List<Customer> findAll() {
    return repository.getData();
  }

  @PostMapping(path = "/", consumes = "application/json", produces = "application/json")
  public ResponseEntity<Customer> addCustomer(@RequestBody Customer customer) throws Exception {
    repository.save(customer);
    return new ResponseEntity<Customer>(customer, HttpStatus.CREATED);
  }
}

The Customer class uses the CustomerRepository to persist the Customer object. Check at the end of the article for the full code for this example.

You can run the class with a basic SpringBootApplication:

package com.example.testrest;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {
  public static void main(String[] args) {
    SpringApplication.run(DemoApplication.class, args);
  }
}

In order to test it, we can use the TestRestTemplate class:

package com.example.testrest;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;
import static org.junit.Assert.assertEquals;
@RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) public class DemoApplicationTests {
  private static final ObjectMapper om = new ObjectMapper();
  @Autowired private TestRestTemplate restTemplate;
  @Test public void testCustomerList() throws Exception {
    ResponseEntity < String > response = restTemplate.withBasicAuth("user", "password").getForEntity("/", String.class);
    printJSON(response);
    //Verify user is authorized and content is JSON 	
    assertEquals(MediaType.APPLICATION_JSON_UTF8, response.getHeaders().getContentType());
    assertEquals(HttpStatus.OK, response.getStatusCode());
    Customer c = new Customer(3, "Adam");
    ResponseEntity < String > result = restTemplate.withBasicAuth("user", "password").postForEntity("/", c, String.class);

    //Verify user is unauthorized 		

    Assert.assertEquals(403, result.getStatusCodeValue()); //Verify user is authorized 		

    result = restTemplate.withBasicAuth("admin", "password").postForEntity("/", c, String.class);

    //Verify request succeed 		

    Assert.assertEquals(201, result.getStatusCodeValue());
  }
  private static void printJSON(Object object) {
    String result;
    try {
      result = om.writerWithDefaultPrettyPrinter().writeValueAsString(object);
      System.out.println(result);
    } catch (JsonProcessingException e) {
      e.printStackTrace();
    }
  }
}

Within this test, we execute a set of assertions. At first we execute an HTTP GET using the “user/password” credentials. Then, using the same credentials we attempt to execute an HTTP POST. This test is expected to fail. Finally, we execute again the HTTP POST with the “admin/password” credentials. This is expected to pass.

Run the Test class

You can run the above test with:

mvn install   

You should see that the test method completes successufully:

[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 8.755 s - in com.example.testrest.DemoApplicationTests 

Congratulations! You just managed to run your first basic example of Spring in-memory Authentication against a REST Service.

Full source code: https://github.com/fmarchioni/masterspringboot/tree/master/security/rest-inmemory-security