In this tutorial, we will build Login and Registration REST API using Spring Boot 3, Spring Security, and MySQL database.
We will create a Registration REST API that will save a registered User in the MySQL database.
Next, we will create a Login REST API so that registered users can log-in using Login REST API.
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
<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)
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
public interface UserRepository extends JpaRepository<User, Long> { } public interface RoleRepository extends JpaRepository<Role, Long> { Role findByName(String name); }
5. Spring Security Implementation
CustomUserDetailsService
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
@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 and RegisterDto
LoginDto
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;
RegisterDto
@Getter @Setter @NoArgsConstructor @AllArgsConstructor public class RegisterDto { private String name; private String username; private String email; private String password; }
7. Creating a Service Layer
AuthService
public interface AuthService { String register(RegisterDto registerDto); String login(LoginDto loginDto); }
AuthServiceImpl
@Service @AllArgsConstructor public class AuthServiceImpl implements AuthService { private UserRepository userRepository; private RoleRepository roleRepository; private PasswordEncoder passwordEncoder; private AuthenticationManager authenticationManager; @Override public String register(RegisterDto registerDto) { // check username is already exists in database if(userRepository.existsByUsername(registerDto.getUsername())){ throw new TodoAPIException(HttpStatus.BAD_REQUEST, "Username already exists!"); } // check email is already exists in database if(userRepository.existsByEmail(registerDto.getEmail())){ throw new TodoAPIException(HttpStatus.BAD_REQUEST, "Email is already exists!."); } User user = new User(); user.setName(registerDto.getName()); user.setUsername(registerDto.getUsername()); user.setEmail(registerDto.getEmail()); user.setPassword(passwordEncoder.encode(registerDto.getPassword())); Set<Role> roles = new HashSet<>(); Role userRole = roleRepository.findByName("ROLE_USER"); roles.add(userRole); user.setRoles(roles); userRepository.save(user); return "User Registered Successfully!."; } @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 and Registration REST API
@AllArgsConstructor @RestController @RequestMapping("/api/auth") public class AuthController { private AuthService authService; // Build Register REST API @PostMapping("/register") public ResponseEntity<String> register(@RequestBody RegisterDto registerDto){ String response = authService.register(registerDto); return new ResponseEntity<>(response, HttpStatus.CREATED); } // Build Login REST API @PostMapping("/login") public ResponseEntity<String> login(@RequestBody LoginDto loginDto){ String token = authService.login(loginDto); return new ResponseEntity<>(token, 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);
Comments
Post a Comment