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
- 
Open Spring Initializr:
- Go to Spring Initializr in your web browser.
 
 - 
Configure Project Metadata:
- Project: Maven Project
 - Language: Java
 - Spring Boot: Select the latest version of Spring Boot
 - Group: com.example
 - Artifact: spring-boot-react-auth
 - Name: spring-boot-react-auth
 - Description: Full Stack Application with Spring Boot and React for User Registration and Login
 - Package Name: com.example.springbootreactauth
 - Packaging: Jar
 - Java Version: 17 (or your preferred version)
 - Click 
Next. 
 - 
Select Dependencies:
- On the 
Dependenciesscreen, select the dependencies you need. For user authentication, you can start with:- Spring Web
 - Spring Security
 - Spring Data JPA
 - H2 Database
 - Spring Boot DevTools
 
 - Click 
Next. 
 - On the 
 - 
Generate the Project:
- Click 
Generateto download the project zip file. - Extract the zip file to your desired location.
 
 - Click 
 - 
Open the Project in Your IDE:
- Open your IDE and import the project as a Maven project.
 
 
1.2 Project Structure
After importing the project, you will see the following structure in your IDE:
spring-boot-react-auth
├── src
│   ├── main
│   │   ├── java
│   │   │   └── com
│   │   │       └── example
│   │   │           └── springbootreactauth
│   │   │               ├── SpringBootReactAuthApplication.java
│   │   │               ├── config
│   │   │               ├── controller
│   │   │               ├── model
│   │   │               ├── repository
│   │   │               └── service
│   ├── main
│   │   └── resources
│   │       ├── application.properties
│   └── test
│       └── java
│           └── com
│               └── example
│                   └── springbootreactauth
│                       └── SpringBootReactAuthApplicationTests.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
2.2 Create the User Entity
In the model package, create a new Java class named User:
package com.example.springbootreactauth.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.springbootreactauth.repository;
import com.example.springbootreactauth.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    User findByUsername(String username);
}
2.4 Create the UserService Interface
In the service package, create a new Java interface named UserService:
package com.example.springbootreactauth.service;
import com.example.springbootreactauth.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.springbootreactauth.service;
import com.example.springbootreactauth.model.User;
import com.example.springbootreactauth.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 Configure Spring Security
Create a new Java class named SecurityConfig in the config package:
package com.example.springbootreactauth.config;
import com.example.springbootreactauth.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;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
    private final UserServiceImpl userService;
    public SecurityConfig(UserServiceImpl userService) {
        this.userService = userService;
    }
    @Bean
    public UserDetailsService userDetailsService() {
        return username -> {
            User 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/register", "/api/login").permitAll()
                    .anyRequest().authenticated()
            )
            .httpBasic();
        return http.build();
    }
}
2.7 Create the UserController Class
In the controller package, create a new Java class named UserController:
package com.example.springbootreactauth.controller;
import com.example.springbootreactauth.model.User;
import com.example.springbootreactauth.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api")
public class UserController {
    private final UserService userService;
    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }
    @PostMapping("/register")
    public String register(@RequestBody User user) {
        User existingUser = userService.findByUsername(user.getUsername());
        if (existingUser != null) {
            return "Username already exists";
        }
        userService.saveUser(user);
        return "User registered successfully";
    }
    @
PostMapping("/login")
    public String login(@RequestBody User user) {
        User existingUser = userService.findByUsername(user.getUsername());
        if (existingUser != null && user.getPassword().equals(existingUser.getPassword())) {
            return "Login successful";
        } else {
            return "Invalid credentials";
        }
    }
}
Step 3: Creating the Frontend with React
3.1 Set Up React Project
- 
Open a terminal and navigate to your workspace directory.
 - 
Create a new React project using Create React App:
npx create-react-app react-frontend - 
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 Create Components
Create the necessary components for the user registration and login functionalities.
3.3.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 {
    register(user) {
        return axios.post(`${API_BASE_URL}/register`, user);
    }
    login(credentials) {
        return axios.post(`${API_BASE_URL}/login`, credentials);
    }
}
export default new AuthService();
3.3.2 Create RegisterComponent.js
Create a new file RegisterComponent.js in the src/components directory:
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import AuthService from '../AuthService';
const RegisterComponent = () => {
    const [username, setUsername] = useState('');
    const [password, setPassword] = useState('');
    const [message, setMessage] = useState('');
    const navigate = useNavigate();
    const handleRegister = async (e) => {
        e.preventDefault();
        try {
            const response = await AuthService.register({ username, password });
            setMessage(response.data);
            if (response.data === 'User registered successfully') {
                navigate('/login');
            }
        } catch (error) {
            setMessage('Registration failed');
        }
    };
    return (
        <div className="container">
            <div className="row justify-content-center">
                <div className="col-md-6">
                    <div className="card">
                        <div className="card-header">Register</div>
                        <div className="card-body">
                            {message && <div className="alert alert-info">{message}</div>}
                            <form onSubmit={handleRegister}>
                                <div className="form-group">
                                    <label>Username</label>
                                    <input
                                        type="text"
                                        className="form-control"
                                        value={username}
                                        onChange={(e) => setUsername(e.target.value)}
                                    />
                                </div>
                                <div className="form-group">
                                    <label>Password</label>
                                    <input
                                        type="password"
                                        className="form-control"
                                        value={password}
                                        onChange={(e) => setPassword(e.target.value)}
                                    />
                                </div>
                                <button type="submit" className="btn btn-primary">Register</button>
                            </form>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    );
};
export default RegisterComponent;
3.3.3 Create LoginComponent.js
Create a new file LoginComponent.js in the src/components directory:
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import AuthService from '../AuthService';
const LoginComponent = () => {
    const [username, setUsername] = useState('');
    const [password, setPassword] = useState('');
    const [message, setMessage] = useState('');
    const navigate = useNavigate();
    const handleLogin = async (e) => {
        e.preventDefault();
        try {
            const response = await AuthService.login({ username, password });
            if (response.data === 'Login successful') {
                navigate('/dashboard');
            } else {
                setMessage('Invalid credentials');
            }
        } catch (error) {
            setMessage('Invalid credentials');
        }
    };
    return (
        <div className="container">
            <div className="row justify-content-center">
                <div className="col-md-6">
                    <div className="card">
                        <div className="card-header">Login</div>
                        <div className="card-body">
                            {message && <div className="alert alert-danger">{message}</div>}
                            <form onSubmit={handleLogin}>
                                <div className="form-group">
                                    <label>Username</label>
                                    <input
                                        type="text"
                                        className="form-control"
                                        value={username}
                                        onChange={(e) => setUsername(e.target.value)}
                                    />
                                </div>
                                <div className="form-group">
                                    <label>Password</label>
                                    <input
                                        type="password"
                                        className="form-control"
                                        value={password}
                                        onChange={(e) => setPassword(e.target.value)}
                                    />
                                </div>
                                <button type="submit" className="btn btn-primary">Login</button>
                            </form>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    );
};
export default LoginComponent;
3.3.4 Create DashboardComponent.js
Create a new file DashboardComponent.js in the src/components directory:
import React from 'react';
const DashboardComponent = () => {
    return (
        <div>
            <h2>Dashboard</h2>
            <p>Welcome to the dashboard!</p>
        </div>
    );
};
export default DashboardComponent;
3.3.5 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 RegisterComponent from './components/RegisterComponent';
import LoginComponent from './components/LoginComponent';
import DashboardComponent from './components/DashboardComponent';
const App = () => {
    return (
        <Router>
            <div className="container">
                <Routes>
                    <Route path="/" element={<LoginComponent />} />
                    <Route path="/register" element={<RegisterComponent />} />
                    <Route path="/login" element={<LoginComponent />} />
                    <Route path="/dashboard" element={<DashboardComponent />} />
                </Routes>
            </div>
        </Router>
    );
};
export default App;
3.3.6 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
- Open the 
SpringBootReactAuthApplicationclass in thesrc/main/java/com/example/springbootreactauthdirectory. - Click the green 
Runbutton in your IDE or use the terminal to run the application:./mvnw spring-boot:run 
4.2 Run the React Application
- 
Open a terminal and navigate to the
react-frontenddirectory. - 
Start the React application:
npm start - 
Open your web browser and navigate to
http://localhost:3000. 
You can now use the registration and login functionalities 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 user registration and login functionalities using Spring Security 6+ and created a simple registration and login page with React. This setup provides a solid foundation for developing more complex full-stack applications with user authentication.
Comments
Post a Comment