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()); } }
Download
From:一号门
COMMENTS