Java Spring-Boot and Mysql User Authentication API Tutorial in VS Code

Java Spring-Boot Knowledge Sharing #2

Geno Tech
6 min readMay 30, 2023
Java Spring-Boot and Mysql User Authentication API Tutorial in VS Code

This article will implement an authentication API using Spring-Boot and MySQL in VS Code editor. Security is a main concern in all applications; therefore, we should pay attention to it. These are the clear steps you want to follow to achieve it. Finally, you can see the result in Postman. Here I did the implementations in VS Code editor. You can start with VS code without having any prior knowledge. When you are implementing from scratch, you will face some technical issues. Please comment on them here before it comes to a headache. Let’s follow the steps below.

Prerequisites

  • Set up Spring Boot in VS Code.
  • Install MySQL Workbench.
  • Install Postman.

Here are the prerequisites you should have installed. If you want help to initiate a new Spring Boot project in VS Code, please follow my article below.

Step 01: Create the new Spring Boot Project

Create a new project, as in the following video. When you add the dependencies, please add the following two additional dependencies.

  <dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>

Step 02: Add Required Dependencies to the pom.xml

We are ready to do implementations. First, check the pom.xml file to confirm whether you added the required dependencies.

<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-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</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.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
</dependencies>

Step 03: Create the Folder Structure & New Files

Create the Folder Structure & New Files

Please create the new folders and files according to this.

Step 04: Code Implementation

Then we will update all the files we created according to the above image.

*** SecurityConfig.Java ***

package com.example.authentication.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableMethodSecurity
public class SecurityConfig {


private UserDetailsService userDetailsService;

public SecurityConfig(UserDetailsService userDetailsService){
this.userDetailsService = userDetailsService;
}

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

@Bean
public AuthenticationManager authenticationManager(
AuthenticationConfiguration configuration) throws Exception {
return configuration.getAuthenticationManager();
}

@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf(csrf -> csrf.disable())
.authorizeHttpRequests((authorize) ->
//authorize.anyRequest().authenticated()
authorize.requestMatchers(HttpMethod.GET, "/api/**").permitAll()
.requestMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()

);

return http.build();
}
}

*** AuthController.Java ***

package com.example.authentication.controller;
import com.example.authentication.entity.Role;
import com.example.authentication.entity.User;
import com.example.authentication.payload.LoginDto;
import com.example.authentication.payload.SignUpDto;
import com.example.authentication.repository.RoleRepository;
import com.example.authentication.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
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.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Collections;

@RestController
@RequestMapping("/api/auth")
public class AuthController {

@Autowired
private AuthenticationManager authenticationManager;

@Autowired
private UserRepository userRepository;

@Autowired
private RoleRepository roleRepository;

@Autowired
private PasswordEncoder passwordEncoder;

@PostMapping("/signin")
public ResponseEntity<String> authenticateUser(@RequestBody LoginDto loginDto){
Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(
loginDto.getUsernameOrEmail(), loginDto.getPassword()));

SecurityContextHolder.getContext().setAuthentication(authentication);
return new ResponseEntity<>("User signed-in successfully!.", HttpStatus.OK);
}

@PostMapping("/signup")
public ResponseEntity<?> registerUser(@RequestBody SignUpDto signUpDto){

// System.out.println("Here");
// add check for username exists in a DB
if(userRepository.existsByUsername(signUpDto.getUsername())){
return new ResponseEntity<>("Username is already taken!", HttpStatus.BAD_REQUEST);
}

// add check for email exists in DB
if(userRepository.existsByEmail(signUpDto.getEmail())){
return new ResponseEntity<>("Email is already taken!", HttpStatus.BAD_REQUEST);
}

// create user object
User user = new User();
user.setName(signUpDto.getName());
user.setUsername(signUpDto.getUsername());
user.setEmail(signUpDto.getEmail());
user.setPassword(passwordEncoder.encode(signUpDto.getPassword()));

Role roles = roleRepository.findByName("ROLE_ADMIN").get();
user.setRoles(Collections.singleton(roles));

userRepository.save(user);

return new ResponseEntity<>("User registered successfully", HttpStatus.OK);

}
}

Step 05: Create the Entities

*** Role.Java ***

package com.example.authentication.entity;

import lombok.Getter;
import lombok.Setter;

import jakarta.persistence.*;

@Setter
@Getter
@Entity
@Table(name = "roles")
public class Role {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;

@Column(length = 60)
private String name;
}

*** User.Java ***

package com.example.authentication.entity;

import lombok.Data;

import jakarta.persistence.*;
import java.util.Set;

@Data
@Entity
@Table(name = "users", uniqueConstraints = {
@UniqueConstraint(columnNames = {"username"}),
@UniqueConstraint(columnNames = {"email"})
})
public class User {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
private String username;
private String email;
private String password;

@ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinTable(name = "user_roles",
joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id"))
private Set<Role> roles;
}

Step 06: Create the DTO Codes

*** LoginDto.Java ***

package com.example.authentication.payload;

import lombok.Data;

@Data
public class LoginDto {
private String usernameOrEmail;
private String password;
}

*** SignUpDto.Java ***

package com.example.authentication.payload;

import lombok.Data;

@Data
public class SignUpDto {
private String name;
private String username;
private String email;
private String password;
}

Step 06: Implement the Repository Codes

*** RoleRepository.Java ***

package com.example.authentication.repository;

import com.example.authentication.entity.Role;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface RoleRepository extends JpaRepository<Role, Long> {
Optional<Role> findByName(String name);
}

*** UserRepository.Java ***

package com.example.authentication.repository;

import com.example.authentication.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
Optional<User> findByUsernameOrEmail(String username, String email);
Optional<User> findByUsername(String username);
Boolean existsByUsername(String username);
Boolean existsByEmail(String email);
}

Step 07: Implement the Repository Codes

*** CustomUserDetailsService.Java ***

package com.example.authentication.service;

import com.example.authentication.entity.User;
import com.example.authentication.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
public class CustomUserDetailsService implements UserDetailsService {

private UserRepository userRepository;

public CustomUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}

@Override
public UserDetails loadUserByUsername(String usernameOrEmail) throws UsernameNotFoundException {
User user = userRepository.findByUsernameOrEmail(usernameOrEmail, usernameOrEmail)
.orElseThrow(() ->
new UsernameNotFoundException("User not found with username or email: "+ usernameOrEmail));

Set<GrantedAuthority> authorities = user
.getRoles()
.stream()
.map((role) -> new SimpleGrantedAuthority(role.getName())).collect(Collectors.toSet());

return new org.springframework.security.core.userdetails.User(user.getEmail(),
user.getPassword(),
authorities);
}
}

Step 08: Edit the Application.properties File

spring.application.name=authentication

server.port=8084

spring.jpa.hibernate.ddl-auto=update
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

spring.datasource.url=jdbc:mysql://localhost:3306/myblog?createDatabaseIfNotExist=true&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root

#jpa vendor adapter configuration
spring.jpa.database-platform=org.hibernate.dialect.MySQL57Dialect
spring.jpa.generate-ddl=true
spring.jpa.show-sql=true

Step 09: Run the Project

Now we have to run the project and see the results. Finally, the AuthenticationApplication.java file looks like this.

package com.example.authentication;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class AuthenticationApplication {

public static void main(String[] args) {
SpringApplication.run(AuthenticationApplication.class, args);
}

}

If the project has run successfully, you can see the following view of the terminal. Otherwise, your app has some errors which need to fix.

Step 10: Check the Results in Postman

Finally, we are going to check all using Postman. We have to check 2 API calls, SingIn and SignUp.

  • Sing Up a new User
  • Sign In a new User

Step 11: Database & Tables are Created

In the end, the database & the table are created as follows. I used the MySQL workbench here.

Database & Tables are Created

All done and you can see the results successfully. If yo have any error, please comment here, and then I will answer your response.

Happy Coding !!!!
Found this post useful? Kindly tap the 👏 button below! :)

--

--

Geno Tech

Software Development | Data Science | AI — We write rich & meaningful content on development, technology, digital transformation & life lessons.