Spring Boot Security Login REST API Example

In this tutorial, we will build Login REST API using Spring Boot 3, Spring Security, and MySQL database. We will create a Login REST API that will authenticate the request, and return a success message in the response.

Spring Security is a framework that provides authentication, authorization, and protection against common attacks. With first-class support for securing both web and reactive applications, it is the de-facto standard for securing Spring-based applications.

1. Add Maven Dependencies

Add below Maven dependencies to your Spring Boot project:
<dependency>	<groupId>org.springframework.boot</groupId>	<artifactId>spring-boot-starter-data-jpa</artifactId>	</dependency>	<dependency>	<groupId>org.springframework.boot</groupId>	<artifactId>spring-boot-starter-web</artifactId>	</dependency>	<dependency>	<groupId>com.mysql</groupId>	<artifactId>mysql-connector-j</artifactId>	<scope>runtime</scope>	</dependency>	<dependency>	<groupId>org.projectlombok</groupId>	<artifactId>lombok</artifactId>	<optional>true</optional>	</dependency>	<dependency>	<groupId>org.springframework.boot</groupId>	<artifactId>spring-boot-starter-test</artifactId>	<scope>test</scope>	</dependency>	<dependency>	<groupId>org.springframework.boot</groupId>	<artifactId>spring-boot-starter-security</artifactId>	</dependency> 

2. Configure MySQL Database

Let's first create a database in MySQL server using the below command:

create database login_system 

Since we’re using MySQL as our database, we need to configure the database URL, username, and password so that Spring can establish a connection with the database on startup. Open the src/main/resources/application.properties file and add the following properties to it:

spring.datasource.url=jdbc:mysql://localhost:3306/login_system spring.datasource.username=root spring.datasource.password=Mysql@123 spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect spring.jpa.hibernate.ddl-auto=update 

3. Create JPA Entities - User and Role (Many-to-Many Mapping)

In this step, we will create User and Role JPA entities and establish MANY-to-MANY relationships between them. Let's use JPA annotations to establish MANY-to-MANY relationships between User and Role entities.

User

import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import java.util.Set; @Setter @Getter @NoArgsConstructor @AllArgsConstructor @Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; @Column(nullable = false, unique = true) private String username; @Column(nullable = false, unique = true) private String email; @Column(nullable = false) private String password; @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL) @JoinTable(name = "users_roles", joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id") ) private Set<Role> roles; } 

Role

import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @Getter @Setter @NoArgsConstructor @AllArgsConstructor @Entity @Table(name = "roles") public class Role { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; } 

4. Create Spring Data JPA Repositories

Next, let's create a repository package. Within a repository package, we create UserRepository and RoleRepository interfaces.
public interface UserRepository extends JpaRepository<User, Long> { } public interface RoleRepository extends JpaRepository<Role, Long> { Role findByName(String name); }

5. Spring Security Implementation

CustomUserDetailsService

Let's create a CustomUserDetailsService class that implements the UserDetailsService interface ( Spring security in-build interface) and provides an implementation for the loadUserByUername() method:
import lombok.AllArgsConstructor; import net.javaguides.todo.entity.User; import net.javaguides.todo.repository.UserRepository; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; 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; import java.util.Set; import java.util.stream.Collectors; @Service @AllArgsConstructor public class CustomUserDetailsService implements UserDetailsService { private UserRepository userRepository; @Override public UserDetails loadUserByUsername(String usernameOrEmail) throws UsernameNotFoundException { User user = userRepository.findByUsernameOrEmail(usernameOrEmail, usernameOrEmail) .orElseThrow(() -> new UsernameNotFoundException("User not exists by Username or Email")); Set<GrantedAuthority> authorities = user.getRoles().stream() .map((role) -> new SimpleGrantedAuthority(role.getName())) .collect(Collectors.toSet()); return new org.springframework.security.core.userdetails.User( usernameOrEmail, user.getPassword(), authorities ); } } 

SpringSecurityConfig

Let's create a class SpringSecurityConfig and add the following configuration to it:
 @Configuration @EnableMethodSecurity @AllArgsConstructor public class SpringSecurityConfig { private UserDetailsService userDetailsService; @Bean public static PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } @Bean SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.csrf(csrf -> csrf.disable()) .authorizeHttpRequests((authorize) -> { authorize.requestMatchers("/api/auth/**").permitAll(); authorize.anyRequest().authenticated(); }).httpBasic(Customizer.withDefaults()); return http.build(); } @Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception { return configuration.getAuthenticationManager(); } } 

6. Creating DTO class - LoginDto

Let's create a LoginDto class and add the following content to it:
import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @Getter @Setter @NoArgsConstructor @AllArgsConstructor public class LoginDto { private String usernameOrEmail; private String password; } import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; 

7. Creating a Service Layer

Create a service package and add the following service layer-related AuthService interface and AuthServiceImpl class.

AuthService

public interface AuthService { String login(LoginDto loginDto); } 

AuthServiceImpl

import lombok.AllArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; import java.util.HashSet; import java.util.Set; @Service @AllArgsConstructor public class AuthServiceImpl implements AuthService { private AuthenticationManager authenticationManager; @Override public String login(LoginDto loginDto) { Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken( loginDto.getUsernameOrEmail(), loginDto.getPassword() )); SecurityContextHolder.getContext().setAuthentication(authentication); return "Login Successful!"; } } 

8. Controller Layer - Login REST API

This code defines a REST API endpoint for user authentication. It receives a POST request at the "/api/auth/login" URL with the login credentials in the request body as a JSON object. The LoginDto object is used to map the JSON object to a Java object.
import lombok.AllArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @AllArgsConstructor @RestController @RequestMapping("/api/auth") public class AuthController { private AuthService authService; // Build Login REST API @PostMapping("/login") public ResponseEntity<String> login(@RequestBody LoginDto loginDto){ String success = authService.login(loginDto); return new ResponseEntity<>(success, HttpStatus.OK);
} }

9. Insert SQL Scripts

Before testing Spring security, make sure that you use below SQL scripts below to insert the database into respective tables:

INSERT INTO `users` VALUES (1,'ramesh@gmail.com','ramesh','$2a$10$5PiyN0MsG0y886d8xWXtwuLXK0Y7zZwcN5xm82b4oDSVr7yF0O6em','ramesh'), (2,'admin@gmail.com','admin','$2a$10$gqHrslMttQWSsDSVRTK1OehkkBiXsJ/a4z2OURU./dizwOQu5Lovu','admin'); INSERT INTO `roles` VALUES (1,'ROLE_ADMIN'),(2,'ROLE_USER'); INSERT INTO `users_roles` VALUES (2,1),(1,2); 

10. Testing using Postman

Refer to the below screenshot to test the Login REST API using the Admin User:




Comments