Spring Boot React JWT Authentication Example
author : Sai K
In this tutorial, we will create a full-stack application using Spring Boot for the backend and React (using functional components and hooks) for the frontend. We will implement JWT-based authentication using Spring Security 6+ and React 18. The tutorial will cover setting up the project, configuring Spring Security, creating a Spring Boot REST API for user authentication with JWT, and building a React application for the same. We will also use Bootstrap for styling.
Prerequisites
Before we start, ensure you have the following:
- Java Development Kit (JDK) installed
- Apache Maven installed
- Node.js and npm installed
- An IDE (such as IntelliJ IDEA, Eclipse, or VS Code) installed
Step 1: Setting Up the Spring Boot Project
1.1 Create a Spring Boot Project
Go to Spring Initializr n your web browser.
2.Configure Project Metadata:
- Project: Maven Project
- Language: Java
- Spring Boot: Select the latest version of Spring Boot
- Group: com.example
- Artifact: spring-boot-react-jwt
- Name: spring-boot-react-jwt
- Description: Full Stack Application with Spring Boot and React for JWT Authentication
- Package Name: com.example.springbootreactjwt
- Packaging: Jar
- Java Version: 17 (or your preferred version)
- Click Next.
3.Select Dependencies:
- Spring Web
- Spring Security
- Spring Data JPA
- H2 Database
- Spring Boot DevTools
- JSON Web Token (JWT)
4.Generate the Project:
- Click Generate to download the project zip file.
- Extract the zip file to your desired location.
5.Open the Project in Your IDE:
- Open your IDE and import the project as a Maven project.
1.2 Maven Dependencies
Here's a pom.xml file for the Spring Boot and JWT authentication tutorial:
< ?xml version="1.0" encoding="UTF-8"?>
< project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
< modelVersion>4.0.0< /modelVersion>
< groupId>com.example< /groupId>
< artifactId>spring-boot-react-jwt< /artifactId>
< version>0.0.1-SNAPSHOT< /version>
< name>spring-boot-react-jwt< /name>
< description>Full Stack Application with Spring Boot and React for JWT Authentication
< packaging>jar< /packaging>
< parent>
< groupId>org.springframework.boot< /groupId>
< artifactId>spring-boot-starter-parent< /artifactId>
< version>3.0.6< /version>
< relativePath/>
< /parent>
< properties>
< java.version>17< /java.version>
< jjwt.version>0.11.5< /jjwt.version>
< /properties>
< 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>org.springframework.boot< /groupId>
< artifactId>spring-boot-devtools< /artifactId>
< scope>runtime< /scope>
< optional>true< /optional>
< /dependency>
< dependency>
< groupId>com.h2database< /groupId>
< artifactId>h2< /artifactId>
< scope>runtime< /scope>
< /dependency>
< dependency>
< groupId>io.jsonwebtoken< /groupId>
< artifactId>jjwt-api< /artifactId>
< version>${jjwt.version}< /version>
< /dependency>
< dependency>
< groupId>io.jsonwebtoken< /groupId>
< artifactId>jjwt-impl< /artifactId>
< version>${jjwt.version}< /version>
< scope>runtime< /scope>
< /dependency>
< dependency>
< groupId>io.jsonwebtoken< /groupId>
< artifactId>jjwt-jackson< /artifactId>
< version>${jjwt.version}< /version>
< scope>runtime< /scope>
< /dependency>
< /dependencies>
< build>
< plugins>
< plugin>
< groupId>org.springframework.boot< /groupId>
< artifactId>spring-boot-maven-plugin< /artifactId>
< /plugin>
< /plugins>
< /build>
< /project>
I suggest using the same JWT dependency versions as the ones used in this tutorial.
1.3 Project Structure
After importing the project, you will see the following structure in your IDE:
spring-boot-react-jwt
├── src
│ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ └── example
│ │ │ └── springbootreactjwt
│ │ │ ├── SpringBootReactJwtApplication.java
│ │ │ ├── config
│ │ │ ├── controller
│ │ │ ├── model
│ │ │ ├── repository
│ │ │ ├── security
│ │ │ └── service
│ ├── main
│ │ └── resources
│ │ ├── application.properties
│ └── test
│ └── java
│ └── com
│ └── example
│ └── springbootreactjwt
│ └── SpringBootReactJwtApplicationTests.java
└── pom.xml
Step 2: Creating the Backend
2.1 Configure H2 Database
Open the application.properties file located in the src/main/resources directory and add the
following
configuration:
# H2 Database configuration
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.h2.console.enabled=true
# JPA settings
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
# JWT configuration
jwt.secret=your_jwt_secret_key
jwt.expiration=3600000
2.2 Create the User Entity
In the model package, create a new Java class named User:
package com.example.springbootreactjwt.model;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String password;
private String role;
// Getters and Setters
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
}
2.3 Create the UserRepository Interface
In the repository package, create a new Java interface named UserRepository:
package com.example.springbootreactjwt.repository;
import com.example.springbootreactjwt.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository {
User findByUsername(String username);
}
2.4 Create the UserService Interface
In the service package, create a new Java interface named UserService:
package com.example.springbootreactjwt.service;
import com.example.springbootreactjwt.model.User;
public interface UserService {
User findByUsername(String username);
User saveUser(User user);
}
2.5 Implement the UserService Interface
In the service package, create a new Java class named UserServiceImpl:
package com.example.springbootreactjwt.service;
import com.example.springbootreactjwt.model.User;
import com.example.springbootreactjwt.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
@Autowired
public UserServiceImpl(UserRepository userRepository, PasswordEncoder passwordEncoder) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
}
@Override
public User findByUsername(String username) {
return userRepository.findByUsername(username);
}
@Override
public User saveUser(User user) {
user.setPassword(passwordEncoder.encode(user.getPassword()));
return userRepository.save(user);
}
}
2.6 Create JWT Utility Class
In the security package, create a new Java class named JwtTokenUtil:
package com.example.springbootreactjwt.security;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.function.Function;
@Component
public class JwtTokenUtil {
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private Long expiration;
public String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + expiration))
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
public Boolean validateToken(String token, String username) {
final String tokenUsername = getUsernameFromToken(token);
return (tokenUsername.equals(username) && !isTokenExpired(token));
}
public String getUsernameFromToken(String token) {
return getClaimFromToken(token, Claims::getSubject);
}
public Date getExpirationDateFromToken(String token) {
return getClaimFromToken(token, Claims::getExpiration);
}
public T getClaimFromToken(String token, Function claimsResolver) {
final Claims claims = getAllClaimsFromToken(token);
return claimsResolver.apply(claims);
}
private Claims getAllClaimsFromToken(String token) {
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
}
private Boolean isTokenExpired(String token) {
final Date expiration = getExpirationDateFromToken(token);
return expiration.before(new Date());
}
}
2.7 Create JWT Authentication Filter
In the security package, create a new Java class named JwtAuthenticationFilter:
package com.example.springbootreactjwt.security;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private UserDetailsService userDetailsService
;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
String jwt = getJwtFromRequest(request);
if (jwt != null && jwtTokenUtil.validateToken(jwt, jwtTokenUtil.getUsernameFromToken(jwt))) {
String username = jwtTokenUtil.getUsernameFromToken(jwt);
var userDetails = userDetailsService.loadUserByUsername(username);
var authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
chain.doFilter(request, response);
}
private String getJwtFromRequest(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
}
2.8 Configure Spring Security
Create a new Java class named SecurityConfig in the config package:
package com.example.springbootreactjwt.config;
import com.example.springbootreactjwt.security.JwtAuthenticationFilter;
import com.example.springbootreactjwt.service.UserServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private final UserServiceImpl userService;
private final JwtAuthenticationFilter jwtAuthenticationFilter;
public SecurityConfig(UserServiceImpl userService, JwtAuthenticationFilter jwtAuthenticationFilter) {
this.userService = userService;
this.jwtAuthenticationFilter = jwtAuthenticationFilter;
}
@Bean
public UserDetailsService userDetailsService() {
return username -> {
var user = userService.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("User not found");
}
return org.springframework.security.core.userdetails.User
.withUsername(user.getUsername())
.password(user.getPassword())
.roles(user.getRole())
.build();
};
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(authorizeRequests ->
authorizeRequests.requestMatchers("/api/login").permitAll()
.anyRequest().authenticated()
)
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
2.9 Create the AuthController Class
In the controller package, create a new Java class named AuthController:
package com.example.springbootreactjwt.controller;
import com.example.springbootreactjwt.model.User;
import com.example.springbootreactjwt.security.JwtTokenUtil;
import com.example.springbootreactjwt.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api")
public class AuthController {
private final AuthenticationManager authenticationManager;
private final JwtTokenUtil jwtTokenUtil;
private final UserService userService;
@Autowired
public AuthController(AuthenticationManager authenticationManager, JwtTokenUtil jwtTokenUtil, UserService userService) {
this.authenticationManager = authenticationManager;
this.jwtTokenUtil = jwtTokenUtil;
this.userService = userService;
}
@PostMapping("/login")
public String login(@RequestBody User user) {
try {
var authenticationToken = new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword());
var authentication = authenticationManager.authenticate(authenticationToken);
var jwt = jwtTokenUtil.generateToken(authentication.getName());
return jwt;
} catch (AuthenticationException e) {
return "Invalid credentials";
}
}
}
Step 3: Creating the Frontend with React
3.1 Set Up React Project
1.Open a terminal and navigate to your workspace directory.
2.Create a new React project using Create React App:
npx create-react-app react-frontend
3.Navigate to the project directory:
cd react-frontend
3.2 Install Axios and React Router DOM
Install Axios to make HTTP requests and React Router DOM for routing:
npm install axios react-router-dom@6
3.3 Install Bootstrap
Install Bootstrap for styling:
npm install bootstrap
3.4 Create Components
Create the necessary components for the user login functionality.
3.4.1 Create AuthService.js
Create a new file AuthService.js in the src directory to handle API requests:
import axios from 'axios';
const API_BASE_URL = "http://localhost:8080/api";
class AuthService {
login(credentials) {
return axios.post(`${API_BASE_URL}/login`, credentials);
}
}
export default new AuthService();
3.4.2 Create LoginComponent.js
Create a new file LoginComponent.js in the src/components directory:
3.4.3 Create DashboardComponent.js
Create a new file DashboardComponent.js in the src/components directory:
import React from 'react';
const DashboardComponent = () => {
return (
< div className="container mt-5">
< h2>Dashboard< /h2>
< p>Welcome to the dashboard!< /p>
< /div>
);
};
export default DashboardComponent;
3.4.4 Create App.js
Modify the App.js file to set up routing for the application:
import React from 'react';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import LoginComponent from './components/LoginComponent';
import DashboardComponent from './components/DashboardComponent';
const App = () => {
return(
< Router>
< div className="container">
< Routes>
< Route path="/" element={< LoginComponent />} />
< Route path="/login" element={< LoginComponent />} />
< Route path="/dashboard" element={< DashboardComponent />} />
< /Routes>
< /div>
< /Router>
):
};
export default App;
3.4.5 Update index.js
Ensure the index.js file is set up correctly:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import 'bootstrap/dist/css/bootstrap.min.css';
const root
= ReactDOM.createRoot(document.getElementById('root'));
root.render(
< React.StrictMode>
< App />
< /React.StrictMode>
);
Step 4: Running the Application
4.1 Run the Spring Boot Application
1.Open the SpringBootReactJwtApplication class in the src/main/java/com/example/springbootreactjwt directory.
2.Click the green Run button in your IDE or use the terminal to run the application:
./mvnw spring-boot:run
4.2 Run the React Application
1.Open a terminal and navigate to the react-frontend directory.
2.Start the React application:
npm start
3.Open your web browser and navigate to http://localhost:3000.
You can now use the login functionality provided by the React frontend and Spring Boot backend.
Conclusion
In this tutorial, we created a full-stack application using Spring Boot for the backend and React
(with functional
components and hooks) for the frontend. We implemented JWT-based authentication
using Spring Security 6+
and created a simple login page with React. This setup provides a solid
foundation for developing more complex
full-stack applications with JWT authentication.