Capstone Project: RESTful CRUD API with Spring Boot + JWT + MySQL


Table of Contents

  1. Introduction
  2. Project Setup
    • Setting up Spring Boot Application
    • Adding Dependencies
  3. Database Setup (MySQL)
    • Setting Up MySQL Database
    • Configuring MySQL Connection in Spring Boot
  4. Creating the User Model & Repository
  5. JWT Authentication
    • Creating JWT Utility Class
    • JWT Filter
    • Configuring Security Configuration
  6. User Registration and Login
    • Registration API
    • Login API
  7. Implementing CRUD Operations
    • Create, Read, Update, Delete Operations
  8. Exception Handling
  9. Testing the API
  10. Conclusion

1. Introduction

In this project, you will build a simple but powerful RESTful CRUD API using Spring Boot, JWT (JSON Web Token) for secure authentication, and MySQL as the relational database. This project will help you understand how to create secure, efficient, and scalable web services. By the end of the project, you will have a complete Spring Boot application with JWT authentication and basic CRUD functionality.


2. Project Setup

Setting up Spring Boot Application

First, create a new Spring Boot project using your preferred IDE (e.g., IntelliJ IDEA or Spring Tool Suite). You can also use Spring Initializr to generate a base project.

  • Group: com.example
  • Artifact: jwtcrudapi
  • Dependencies: Spring Web, Spring Data JPA, Spring Security, MySQL Driver, Lombok (optional), JWT (for token-based authentication)

pom.xml (Maven) Configuration Example:

xmlCopyEdit<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <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>com.mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.11.2</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>
</dependencies>

Adding Dependencies

Ensure the necessary dependencies are added in the pom.xml or build.gradle. The required dependencies are:

  • Spring Boot Web: For building RESTful APIs.
  • Spring Data JPA: For ORM and database interaction.
  • Spring Security: For securing the application using JWT.
  • JWT: For generating and validating JSON Web Tokens.
  • MySQL: For the database connection.

3. Database Setup (MySQL)

Setting Up MySQL Database

You need a MySQL database to store user data. Here’s how to set it up:

  1. Install MySQL on your system or use a managed MySQL service.
  2. Create a Database: sqlCopyEditCREATE DATABASE jwtcrudapi;
  3. Create a User Table: sqlCopyEditCREATE TABLE users ( id BIGINT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(255) UNIQUE NOT NULL, password VARCHAR(255) NOT NULL, role VARCHAR(50) NOT NULL );

Configuring MySQL Connection in Spring Boot

In the application.properties or application.yml, configure the database connection settings:

propertiesCopyEditspring.datasource.url=jdbc:mysql://localhost:3306/jwtcrudapi
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect

4. Creating the User Model & Repository

Create the User entity to map to the users table and the corresponding JPA repository.

User Entity (User.java)

javaCopyEdit@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(unique = true)
    private String username;
    
    private String password;
    
    private String role; // e.g., "ROLE_USER"

    // Getters and Setters
}

User Repository (UserRepository.java)

javaCopyEditpublic interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByUsername(String username);
}

5. JWT Authentication

In this section, we will create the necessary classes to handle JWT-based authentication.

JWT Utility Class (JwtUtils.java)

This utility class will handle JWT creation and validation.

javaCopyEdit@Component
public class JwtUtils {

    private String jwtSecret = "secretKey";  // This should be in properties

    public String generateJwtToken(Authentication authentication) {
        UserPrincipal userPrincipal = (UserPrincipal) authentication.getPrincipal();
        return Jwts.builder()
                .setSubject(userPrincipal.getUsername())
                .setIssuedAt(new Date())
                .setExpiration(new Date((new Date()).getTime() + 86400000)) // 1 day expiry
                .signWith(SignatureAlgorithm.HS512, jwtSecret)
                .compact();
    }

    public String getUsernameFromJwtToken(String token) {
        return Jwts.parser()
                .setSigningKey(jwtSecret)
                .parseClaimsJws(token)
                .getBody()
                .getSubject();
    }

    public boolean validateJwtToken(String authToken) {
        try {
            Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken);
            return true;
        } catch (JwtException e) {
            return false;
        }
    }
}

JWT Filter (JwtAuthenticationFilter.java)

This filter will intercept each request to validate the JWT token.

javaCopyEditpublic class JwtAuthenticationFilter extends OncePerRequestFilter {

    private JwtUtils jwtUtils;

    public JwtAuthenticationFilter(JwtUtils jwtUtils) {
        this.jwtUtils = jwtUtils;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {

        String jwt = getJwtFromRequest(request);
        if (StringUtils.hasText(jwt) && jwtUtils.validateJwtToken(jwt)) {
            String username = jwtUtils.getUsernameFromJwtToken(jwt);
            // Set Authentication for Security Context
            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(username, null, new ArrayList<>());
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }
        filterChain.doFilter(request, response);
    }

    private String getJwtFromRequest(HttpServletRequest request) {
        String header = request.getHeader("Authorization");
        if (StringUtils.hasText(header) && header.startsWith("Bearer ")) {
            return header.substring(7);
        }
        return null;
    }
}

Security Configuration (WebSecurityConfig.java)

javaCopyEdit@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    private JwtUtils jwtUtils;

    @Autowired
    public WebSecurityConfig(JwtUtils jwtUtils) {
        this.jwtUtils = jwtUtils;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeRequests()
            .antMatchers("/auth/**").permitAll()
            .anyRequest().authenticated()
            .and()
            .addFilterBefore(new JwtAuthenticationFilter(jwtUtils), UsernamePasswordAuthenticationFilter.class);
    }
}

6. User Registration and Login

Registration API (AuthController.java)

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

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private JwtUtils jwtUtils;

    @Autowired
    private UserRepository userRepository;

    @PostMapping("/register")
    public ResponseEntity<String> registerUser(@RequestBody User user) {
        // Validate user data, save to DB
        userRepository.save(user);
        return ResponseEntity.ok("User registered successfully");
    }

    @PostMapping("/login")
    public ResponseEntity<?> authenticateUser(@RequestBody LoginRequest loginRequest) {
        // Authenticate user and generate JWT token
        Authentication authentication = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword())
        );
        String jwt = jwtUtils.generateJwtToken(authentication);
        return ResponseEntity.ok(new JwtResponse(jwt));
    }
}

7. Implementing CRUD Operations

Now, you can implement the CRUD operations. For simplicity, let’s create a Product resource that the user can interact with.

Product Model (Product.java)

javaCopyEdit@Entity
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private Double price;

    // Getters and Setters
}

Product Repository (ProductRepository.java)

javaCopyEditpublic interface ProductRepository extends JpaRepository<Product, Long> {
}

Product Controller (ProductController.java)

javaCopyEdit@RestController
@RequestMapping("/products")
public class ProductController {

    @Autowired
    private ProductRepository productRepository;

    @GetMapping
    public List<Product> getAllProducts() {
        return productRepository.findAll();
    }

    @PostMapping
    public Product createProduct(@RequestBody Product product) {
        return productRepository.save(product);
    }

    @PutMapping("/{id}")
    public ResponseEntity<Product> updateProduct(@PathVariable Long id, @RequestBody Product productDetails) {
        Product product = productRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("Product not found"));
        product.setName(productDetails.getName());
        product.setPrice(productDetails.getPrice());
        productRepository.save(product);
        return ResponseEntity.ok(product);
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteProduct(@PathVariable Long id) {
        Product product = productRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("Product not found"));
        productRepository.delete(product);
        return ResponseEntity.noContent().build();
    }
}

8. Exception Handling

Add a centralized exception handler using @ControllerAdvice to handle errors globally.

javaCopyEdit@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<?> handleResourceNotFound(ResourceNotFoundException ex) {
        return new ResponseEntity<>(ex.getMessage(), HttpStatus.NOT_FOUND);
    }
}

9. Testing the API

You can test the API using tools like Postman or Swagger. For instance:

  • Register User: POST /auth/register
  • Login User: POST /auth/login
  • CRUD Operations: GET /products, POST /products, PUT /products/{id}, DELETE /products/{id}

10. Conclusion

This project demonstrates how to build a secure RESTful API with Spring Boot, JWT for authentication, and MySQL for data storage. By using JWT, we ensure stateless and secure authentication for the API. This can be the foundation for more advanced applications that require secure user authentication and CRUD operations.