Spring Security In Memory Authentication Example
This tutorial demonstrates how to configure Spring Security to use In Memory Authentication. We can easily customize the Spring Security AuthenticationManager
to use Spring Security in memory authentication and add multiple users with different attributes, authorities and roles. In this example we used HTTP Basic Authentication
with stateless
configuration for securing rest full web services. We also demonstrate how to create some Integration Tests using MockMvc
.
Project Structure
Let’s start by looking at the project structure.
Maven Dependencies
We use Apache Maven to manage our project dependencies. Make sure the following dependencies reside on the class-path.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.memorynotfound.spring.security</groupId>
<artifactId>inmemory</artifactId>
<version>1.0.0-SNAPSHOT</version>
<url>https://memorynotfound.com</url>
<name>Spring Security - ${project.artifactId}</name>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.8.RELEASE</version>
</parent>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</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-security</artifactId>
</dependency>
<!-- testing -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Spring Security In Memory Authentication
Since we are securing rest services using HTTP Basic Authentication
we disable the csrf – Cross Site Request Forgery settings and secure each request.
We can use the AuthenticationManagerBuilder
with the InMemoryUserDetailsManagerConfigurer
to configure the Spring Security In Memory Authentication. Using the builder pattern we can create multiple users with different attributes, authorities and roles. This automatically configures a UserDetailsService
which we can use.
package com.memorynotfound.spring.security.config;
import org.springframework.context.annotation.Configuration;
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;
import org.springframework.security.config.http.SessionCreationPolicy;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user")
.password("password")
.roles("USER")
.and()
.withUser("manager")
.password("password")
.credentialsExpired(true)
.accountExpired(true)
.accountLocked(true)
.authorities("WRITE_PRIVILEGES", "READ_PRIVILEGES")
.roles("MANAGER");
}
}
The equivalent Spring XML Configuration spring-security-config.xml
is located in the src/main/resources/
folder. You can use this configuration if you prefer XML Configuration over Java 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"
xsi:schemaLocation="http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<http create-session="stateless">
<csrf disabled="true"/>
<intercept-url pattern="/**" access="isAuthenticated()"/>
<http-basic />
</http>
<authentication-manager>
<authentication-provider>
<user-service>
<user name="user"
password="password"
authorities="ROLE_USER" />
<user name="manager"
password="password"
disabled="true"
locked="true"
authorities="WRITE_PRIVILEGES, READ_PRIVILEGES, ROLE_MANAGER" />
</user-service>
</authentication-provider>
</authentication-manager>
</beans:beans>
Rest Service
We created a simple rest service which is protected. We can obtain the current in memory user by injecting the Authentication
as an argument of the method. We can also use the UserDetailsService
which is an instance of the InMemoryUserDetailsManager
if you din’t registered a custom one.
package com.memorynotfound.spring.security.web;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HomeController {
@Autowired
private UserDetailsService userService;
@GetMapping("/")
public String greeting(Authentication authentication) {
return "Spring Security In-memory Authentication Example";
}
}
Spring Boot
We use Spring Boot to start our application.
package com.memorynotfound.spring.security;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;
@SpringBootApplication
// enable if you want to use Spring XML Configuration
// @ImportResource("classpath:spring-security-config.xml")
public class Run {
public static void main(String[] args) {
SpringApplication.run(Run.class, args);
}
}
Demo
Access http://localhost:8080/
without a correct Authorization
header.
Access http://localhost:8080/
with a correct Authorization
header.
Integration Testing
We can write some integration tests using spring-test
and MockMvc
.
package com.memorynotfound.spring.security.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest
@AutoConfigureMockMvc
@RunWith(SpringJUnit4ClassRunner.class)
public class InMemoryHttpBasicIT {
@Autowired
private MockMvc mockMvc;
@Test
public void accessProtected() throws Exception {
this.mockMvc.perform(get("/"))
.andExpect(unauthenticated())
.andExpect(status().isUnauthorized());
}
@Test
public void loginUser() throws Exception {
this.mockMvc.perform(get("/")
.with(httpBasic("user", "password")))
.andExpect(status().isOk());
}
@Test
public void loginInvalidUser() throws Exception {
this.mockMvc.perform(formLogin().user("invalid").password("invalid"))
.andExpect(unauthenticated());
}
}
References
- Spring Security Documentation
- Spring Security HTTP Basic Authentication
- AuthenticationManagerBuilder JavaDoc
- InMemoryUserDetailsManagerConfigurer JavaDoc