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
Found the article helpful? if so please follow us on Socials