Spring Boot Backend Development Course, Implementing Login and Logout with Spring Security, Adding Environment Variables for Testing

This course will provide a detailed explanation of how to develop a backend application using Spring Boot. The main topics include implementing user registration and login/logout features, using Spring Security, and setting up test environment variables.

1. What is Spring Boot?

Spring Boot is a tool that helps make the Spring framework easier to use, allowing for quick and easy application development. By using Spring Boot, applications can be easily run with minimal configuration, and it provides a variety of starter dependencies to enhance development convenience.

2. Introduction to Spring Security

Spring Security is a framework responsible for securing Spring-based applications. It provides authentication and authorization features to strengthen the application’s security. In this course, we will implement user registration, login, and logout through Spring Security.

3. Project Setup

To start the project, visit the Spring Boot initialization website (Spring Initializr), add the necessary dependencies, and create a new project. The following are the key dependencies to add:

  • Spring Web
  • Spring Security
  • Spring Data JPA
  • Spring Boot DevTools
  • H2 Database (used for testing)

4. Implementing User Registration Functionality

4.1. Creating the Entity Class

We will create a User entity class for the user registration functionality. This class will be used to store user information.

package com.example.demo.model;

import javax.persistence.*;

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(nullable = false, unique = true)
    private String username;
    
    @Column(nullable = false)
    private String password;
    
    public User() {}
    
    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }
    
    // getters and setters
}

4.2. Creating the Repository Interface

Create a JPA repository for the User entity.

package com.example.demo.repository;

import com.example.demo.model.User;
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository {
    User findByUsername(String username);
}

4.3. Writing the Service Class

We will create a service class to handle the business logic related to users.

package com.example.demo.service;

import com.example.demo.model.User;
import com.example.demo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    
    @Autowired
    private PasswordEncoder passwordEncoder;
    
    public User registerUser(String username, String password) {
        User user = new User(username, passwordEncoder.encode(password));
        return userRepository.save(user);
    }
}

4.4. Implementing the Registration API

We will implement a RESTful API to handle user requests.

package com.example.demo.controller;

import com.example.demo.model.User;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/auth")
public class AuthController {
    @Autowired
    private UserService userService;
    
    @PostMapping("/register")
    public User register(@RequestParam String username, @RequestParam String password) {
        return userService.registerUser(username, password);
    }
}

5. Implementing Login and Logout Functionality

5.1. Configuring Spring Security

We will configure Spring Security and add a filter to handle user authentication.

package com.example.demo.config;

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 SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
            .withUser("user")
            .password(passwordEncoder().encode("password"))
            .roles("USER");
    }
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeRequests()
            .antMatchers("/api/auth/register").permitAll()
            .anyRequest().authenticated()
            .and()
            .httpBasic();
    }
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

5.2. Implementing the Login API

The login feature will be implemented through HTTP Basic Authentication. When a user sends authentication information (username and password) to the `/api/auth/login` endpoint, Spring Security will process it.

5.3. Implementing the Logout API

Logout will be implemented by invalidating the HTTP session. When a user sends a request to the `/api/auth/logout` endpoint, the session will be invalidated.

6. Adding Environment Variables for Testing

To manage sensitive information or configurations in the testing environment, we will create an `application-test.properties` file and set the environment variables.

# application-test.properties
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.hibernate.ddl-auto=create-drop

7. Writing Test Cases

We will add test cases for the API using JUnit and Mockito. The test cases can be written for the UserService and AuthController.

7.1. Testing UserService

package com.example.demo.service;

import com.example.demo.model.User;
import com.example.demo.repository.UserRepository;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
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.crypto.password.PasswordEncoder;

import static org.mockito.Mockito.*;

@SpringBootTest
public class UserServiceTest {
    @InjectMocks
    private UserService userService;
    
    @Mock
    private UserRepository userRepository;
    
    @Mock
    private PasswordEncoder passwordEncoder;
    
    @BeforeEach
    public void setUp() {
        MockitoAnnotations.openMocks(this);
    }
    
    @Test
    public void testRegisterUser() {
        when(passwordEncoder.encode("password")).thenReturn("encodedPassword");
        when(userRepository.save(any(User.class))).thenReturn(new User("username", "encodedPassword"));
        
        User user = userService.registerUser("username", "password");
        
        assertEquals("username", user.getUsername());
        verify(userRepository).save(any(User.class));
    }
}

8. Conclusion

In this course, we learned how to apply Spring Security for user registration, login, and logout functionality in a backend application based on Spring Boot. We also explored how to add test environment variables and write test cases to improve the application’s quality. Based on this foundation, we encourage you to add more complex features and apply them in real-world projects.

9. References