The Spring Security framework secures your application through authentication and authorization. In its default state, Spring Security ensures that each HTTP request path (or page) in your application requires the authentication of a single global user.

This framework is also extremely flexible. It allows you to create customized security rules for each HTTP request path in your application, as well as the different users. So, you can remove the security restriction on pages that don’t require user authorization (such as a home page). And set the roles and authorities of specific user types.

Adding Spring Security to Your Application

There are two ways to add Spring Security to your application. You can either select it as a dependency when generating a new Spring Boot application using Spring initializr, or add it to your build specification file in the dependency section after generating your project.

If you selected one of the Gradle project options, then the dependencies file is build.gradle. However, if you chose Maven, then that file is pom.xml.

Your build.gradle file should contain the following dependency:

 dependencies {
      implementation 'org.springframework.boot:spring-boot-starter-security'
}

While your pom.xml file should contain the following dependency:

 <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-security</artifactId>
</dependency>

The sample application used in the article is available in this GitHub repository and is free for you to use under the MIT license.

Using Spring Security

Once you add the Spring Security dependency to your application, you can start using the framework immediately. Simply execute your application then navigate to Spring Boot’s home page (or any page in your application). The sample application uses the following initial controller to control Spring Boot’s default localhost:8080 request:

 package com.springSecurityDemo.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class WebController {
   
     @GetMapping("/")
      public String home() {
           return "Welcome!";
      }
}

Executing your application after adding the single controller class above generates the following initial view:

Spring Security login page
Created by writer: Kadeisha Kean

You’ll notice that it automatically directs you to the localhost:8080/login page, and it does this before it allows you to access any other page of the application. At this stage, you’ll need to provide the default username (which is the user) and the automatically generated password (which you will find in the console). The console will generate a line like the following:

 Using generated security password: c4070465-4c65-4e72-8c3f-3800e631ba81 

Each time you restart your application the automatically generated password will change, but the username will remain the same. Entering the default username and password will direct you to the appropriate view in your application.

Customizing Spring Security

To customize your application security, you’ll need to override Spring Security’s default configuration. But before that (assuming you already have Spring Web) you’ll need several other dependencies for this sample application:

  • Spring Data JPA
  • MySQL JDBC Driver
  • Thymeleaf
  • Lombok

The Thymeleaf framework will generate different views. Lombok will help to reduce the code in your object classes. The JPA library and MySQL driver will allow you to use a MySQL database with the application, but you do have the option of using any database that you are comfortable with. Using a database means configuring the applications.properties file under the resources file.

 spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/spring_security
spring.datasource.username=root
spring.datasource.password=1234
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

spring.jpa.hibernate.ddl-auto=update

The configuration code above allows you to connect to a local MySQL database called spring_security, with a username of root, and password (1234). You’ll need to update this data to match your database name and credentials.

After adding your additional dependencies and creating your database, you can start deciding how many views your application will have. You’ll also need to know what the security for each page looks like. Our sample application has 6 views:

  • Home Page
  • Registration Page
  • Login Page
  • Logout Page
  • User Page
  • Error Page

The only view that will require user authorization is the user page. This page is only accessible to users that first register, then sign in to the application. In addition to Spring Boot’s default package, you’ll need to create four other packages in your application.

The Registration Controller Class

The controller package will contain the classes that handle HTTP requests. Depending on the function of a page you can usually group each HTTP request into one controller class, as is the case with the WebController class. However, the registration view has more unique functions, thus, it can have a private controller class:

 @Controller
@RequestMapping("/register")
public class RegistrationController {
       private UserRepository userRepo;
       private PasswordEncoder passwordEncoder;
       
       public RegistrationController( UserRepository userRepo, PasswordEncoder passwordEncoder) {
             this.userRepo = userRepo;
             this.passwordEncoder = passwordEncoder;
       }
       @GetMapping
       public String registerForm() {
             return "registration";
       }
       @PostMapping
       public String processRegistration(RegistrationForm form) {
             userRepo.save(form.toUser(passwordEncoder));
             return "redirect:/login";
       }
}

The RegistrationController class is a gateway to the security aspect of your application. The @RequestMapping annotation specifies the type of request that this controller will handle (requests to localhost:8080/register).

The @GetMapping annotation simply indicates that if the application receives a request for /register, the registrationForm() method should handle that request by returning the registration view.

After a visitor clicks the register button, then the @PostMapping annotation comes into play. The processRegistration() method allows you to post the user data that it gets from the RegistrationForm class to the database, using the UserRepository class. But before it stores this data, the processRegistration() method encrypts the user’s password using Spring's PasswordEncoder interface.

Creating New Security Configurations

Since Spring 3.1, developers can now create configurations for Spring Security using Java, which means classes instead of XML. The main thing that these configuration classes require is the @Configuration annotation.

 @Configuration
public class SecurityConfiguration {
}

The @Configuration annotation indicates that the class above is a configuration class. These classes provide beans to the Spring application context, which is a container that Spring uses to create and manage the different components (or beans) of an application. The first bean in the SecurityConfiguration class is the passwordEncoder bean.

 @Bean
public PasswordEncoder passwordEncoder() {
 return new BCryptPasswordEncoder();
}

The RegistrationController class uses the passwordEncoder bean to encode new passwords before saving them to the database. Another important bean that you’ll need to add to the SecurityConfiguration class is the userDetailsService bean.

 @Bean
public UserDetailsService userDetailsService(UserRepository userRepo) {
 return username -> {
   Customer customer = userRepo.findByUsername(username);
     if (customer != null)
           return customer;
     throw new UsernameNotFoundException("Customer '" + username + "' not found");
 };
}

The userDetailsService bean employs Spring Security’s UserDetailsService interface to retrieve a user’s username and password for authentication, during a customer’s login session. So, as soon as a customer clicks the login button in the login view, the userDetailsService bean springs into motion.

Through the UserRepository, the userDetailsService bean gains access to all the existing customers in the database. This interface then uses the UserRepository to locate a user with a matching username and password, then returns all this customer’s attributes as an object.

If the returned object is a customer, then this customer gains access to the application. Otherwise, the page will automatically refresh allowing the user to enter valid credentials.

The Filter Chain

Spring Security's SecurityFilterChain interface is a useful application programming interface (API) that plays an essential role in the Spring Security configuration. This interface works with Spring Security’s HttpSecurity class to create a filter chain for specific HTTP requests.

 @Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  http
  .authorizeHttpRequests((authorize) -> authorize
              .requestMatchers("/user").hasAuthority("USER").anyRequest().permitAll())
  .formLogin(formLogin -> formLogin
              .loginPage("/login").defaultSuccessUrl("/user", true))
  .logout(logout -> logout.logoutSuccessUrl("/logout"));
 return http.build();
}

The filterChain bean above uses the SecurityFilterChain API to accomplish several tasks. First, it uses the HttpSecurity class to dictate that only users that have the role of a USER can access localhost:8080/user. And a user gets this role after registration, thanks to the getAuthorities() method that each new customer object implements.

 @Override
public Collection<? extends GrantedAuthority> getAuthorities() {
 return Arrays.asList(new SimpleGrantedAuthority("USER"));
}

The filter chain allows unauthenticated access to all other URLs in the application. The filterChain bean also utilizes the formLogin() and logout() methods of the HttpSecurity class object.

These methods allow you to automatically direct a user to specific pages after they perform a task. So, a user that enters the correct credentials and clicks the login button on the /login page will be automatically directed to the /user page.

Finally, the filterChain bean builds and returns the filter chain, which allows authorized users to access the application. All three beans in the SecurityConfiguration class work together to secure your application.

However, the filterChain bean plays the more significant role of dictating the authorization level for each HTTP request. As you start to add more pages to your application, you can use the filterChain bean to set their security level.

The Major Benefit of Spring Security

Spring Security gives you complete control over not only who has access to your application, but also the type of access a user can have (through its user roles feature). Access control is one of the single most important aspects of any application. Giving general users unfiltered access to your application due to limited access control barriers might prove to be a costly mistake.