Spring Security Integration with Spring Boot
Last Updated :
30 Aug, 2024
Spring Security is a powerful and customizable authentication and access control framework for Java applications. It provides comprehensive security services for Java EE-based enterprise software applications. This article will integrate Spring Security with a Spring Boot application, covering configuration, authentication, and securing RESTful APIs.
Implementation of Spring Security in a Spring Boot Application
Below is the step-by-step implementation of Spring Security in a Spring Boot application.
Step 1: Add Dependencies
Add the necessary dependencies to your project for Spring Boot and Spring Security.
Maven:
<dependencies>
<!-- Spring Boot Starter Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Boot Starter Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
Gradle:
dependencies {
// Spring Boot Starter Web
implementation 'org.springframework.boot:spring-boot-starter-web'
// Spring Boot Starter Security
implementation 'org.springframework.boot:spring-boot-starter-security'
}
These dependencies include Spring Boot's web and security starters, which provide essential components for web applications and security features.
Step 2: Configure Spring Security
By default, Spring Security secures all endpoints and offers a simple authentication system. Create a configuration class to alter this.
Java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/home").permitAll() // Allow access to home and root
.anyRequest().authenticated() // All other requests need authentication
.and()
.formLogin()
.loginPage("/login") // Custom login page
.permitAll()
.and()
.logout()
.permitAll();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user")
.password(passwordEncoder().encode("password"))
.roles("USER");
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(); // Use BCrypt for password encoding
}
}
Explanation:
- @EnableWebSecurity provides the Spring MVC integration and turns on web security capability for Spring Security.
- @Bean public PasswordEncoder passwordEncoder(), defines a PasswordEncoder bean that uses BCryptPasswordEncoder to encode and debug passwords. A reliable password hashing algorithm isbcryptt.
Step 3: Configure Authentication
Authentication protocols supported by Spring Security include JDBC, LDAP, OAuth2, and in-memory. WebSecurityConfigurerAdapter and overrides the configure(AuthenticationManagerBuilder auth) function to configure authentication.
Java
import org.springframework.context.annotation.Bean;
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.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("regularUser")
.password(passwordEncoder().encode("userPass"))
.roles("USER")
.and()
.withUser("superAdmin")
.password(passwordEncoder().encode("adminPass"))
.roles("ADMIN");
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(); // Use BCrypt for password encoding
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/public/**").permitAll() // Public endpoints accessible without authentication
.anyRequest().authenticated() // All other requests need authentication
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
}
Explanation:
- .antMatchers("/public/**").permitAll(), permits access to any URL beginning with "/public/" without authentication.
- @Bean public PasswordEncoder passwordEncoder(), Uses BCryptPasswordEncoder to define a PasswordEncoder bean. This is a popular option for safely hashing passwords.
Step 4: Secure RESTful APIs
Use the same configure(HttpSecurity HTTP) method to secure RESTful APIs, but adjust the authentication mechanism (e.g., JWT or OAuth2) accordingly.
Java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
public class CustomSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable() // Disable CSRF for stateless APIs
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS) // Stateless session management
.and()
.authorizeRequests()
.antMatchers("/api/admin/**").hasAuthority("ROLE_ADMIN") // Restrict to ADMIN role
.antMatchers("/api/user/**").hasAnyAuthority("ROLE_USER", "ROLE_ADMIN") // Allow USER and ADMIN roles
.antMatchers("/api/public/**").permitAll() // Public endpoints accessible to everyone
.anyRequest().authenticated() // Secure all other endpoints
.and()
.addFilterBefore(customJwtFilter(), UsernamePasswordAuthenticationFilter.class); // Add JWT filter
}
@Bean
public CustomJwtFilter customJwtFilter() {
return new CustomJwtFilter(); // Custom JWT filter for authentication
}
// Additional configuration for JWT or OAuth2 can be added here
}
Explanation:
- .csrf().disable(), Turns off the defense against Cross-Site Request Forgery (CSRF). This is common for stateless APIs, which do not require CSRF.
- .addFilterBefore(customJwtFilter(), Adds a customized JWT filter before the default UsernamePasswordAuthenticationFilter (, UsernamePasswordAuthenticationFilter.class). We'll handle JWT-based authentication with this filter.
Step 5: Make User Details Service
You must put UserDetailsService into practice for a more complex configuration, such as one that uses a database for authentication.
Java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
@Service
public class MyUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository; // Assuming you have a UserRepository to fetch user data
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("User not found");
}
return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), new ArrayList<>());
}
}
Explanation:
- @Service identifies this class as a component of a Spring service. It means you may use dependency injection and component scanning in this class, which also offers business logic.
- @Autowired private UserRepository userRepository injects an instance of UserRepository. It is believed that database activities paboutuser data are handled by this repository.
Step 6: Testing Security Configuration
To make sure your security setup functions as intended, you must test it. The spring-security-test module from Spring Security offers assistance with testing.
Java
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest
@WebMvcTest
public class AuthenticationTests {
@Autowired
private MockMvc mockMvc;
@Test
@WithMockUser(username = "normalUser", roles = {"USER"})
public void testAccessToUserDashboardWithUserRole() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/user/home"))
.andExpect(status().isOk());
}
@Test
@WithMockUser(username = "normalUser", roles = {"USER"})
public void testAccessToAdminDashboardWithUserRole() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/admin/home"))
.andExpect(status().isForbidden());
}
}
Explanation:
- @SpringBootTest indicates that the class is a test for Spring Boot. For integration tests that need to have access to the application context, it launches the entire application backdrop.
- MockMvc allows you to test answers and mimic HTTP requests without having to launch an entire HTTP server.
Note: If you want to go through a sample authentication project implementation of Spring Security, then read this article: Authentication and Authorization in Spring Boot 3.0 with Spring Security
Conclusion
In conclusion, with Spring Security Integration into a Spring Boot application, you can manage authorization and authentication with a strong framework that will protect your application.