Spring Boot Angular Registration and Login Example
author : Sai K
In this tutorial, we will create a Spring Boot backend and an Angular frontend to handle user
registration and
login functionalities. We will use Spring Boot 3.2, Spring Security 6.1 for
security and Angular 17 for the frontend.
The backend will be responsible for handling user data and
authentication, while the frontend will provide a user-
friendly interface for registration and
login.
Prerequisites
Before we start, ensure you have the following:
- Java Development Kit (JDK) installed
- Apache Maven installed
- Node.js and npm installed
- Angular CLI installed (
npm install -g @angular/cli) - An IDE (such as IntelliJ IDEA, Eclipse, or VS Code) installed
Step 1: Setting Up the Spring Boot Backend
1.1 Create a Spring Boot Project
1.Open Spring Initializr:
1. Configure Project Metadata:
- Project: Maven Project
- Language: Java
- Spring Boot: Select the latest version of Spring Boot 3.2
- Group: com.example
- Artifact: user-service
- Name: user-service
- Description: User Service
- Package Name: com.example.userservice
- Packaging: Jar
- Java Version: 17 (or your preferred version)
- Click Next.
3.Select Dependencies:
- Spring Web
- Spring Security
- Spring Data JPA
- H2 Database
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:
1.2 Update application.properties
Open the application.properties file located in the src/main/resources directory and add the
following
configuration:
server.port=8080
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
spring.jpa.hibernate.ddl-auto=update
Explanation:
- server.port=8080: Sets the server port to 8080.
- spring.datasource.url=jdbc:h2:mem:testdb: Configures an in-memory H2 database.
- spring.datasource.driverClassName=org.h2.Driver: Sets the H2 driver class.
- spring.datasource.username=sa and spring.datasource.password=password: Set the H2 database credentials.
- spring.jpa.database-platform=org.hibernate.dialect.H2Dialect: Configures Hibernate to use H2 dialect.
- spring.h2.console.enabled=true: Enables the H2 console for database inspection.
- spring.jpa.hibernate.ddl-auto=update: Automatically updates the database schema based on the JPA entities.
1.3 Create User Entity
Create a User entity class in the com.example.userservice.model package:
package com.example.userservice.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 email;
// Getters and Setters
}
Explanation:
- @Entity: Marks the class as a JPA entity.
- @Id: Marks the id field as the primary key.
- @GeneratedValue(strategy = GenerationType.IDENTITY): Configures auto-increment for the id field.
- username, password, email: Fields representing user attributes.
- Getters and Setters: Methods to access and modify the fields.
1.4 Create User Repository
Create a UserRepository interface in the com.example.userservice.repository package:
package com.example.userservice.repository;
import com.example.userservice.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository {
User findByUsername(String username);
}
Explanation:
- @Repository: Marks the interface as a Spring Data repository.
- Extends JpaRepository<User, Long>: Provides CRUD operations for the User entity.
- findByUsername(String username): Custom query method to find a user by username.
1.5 Create User Service
Create a UserService class in the com.example.userservice.service package that implements UserDetailsService:
package com.example.userservice.service;
import com.example.userservice.model.User;
import com.example.userservice.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
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.stereotype.Service;
import java.util.Optional;
@Service
public class UserService implements UserDetailsService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
@Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
this.passwordEncoder = new BCryptPasswordEncoder();
}
public User saveUser(User user) {
user.setPassword(passwordEncoder.encode(user.getPassword()));
return userRepository.save(user);
}
public Optional findByUsername(String username) {
return Optional.ofNullable(userRepository.findByUsername(username));
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("User not found");
}
return org.springframework.security.core.userdetails.User
.withUsername(user.getUsername())
.password(user.getPassword())
.authorities("USER")
.build();
}
}
Explanation:
- @Service: Marks the class as a service component.
- Implements UserDetailsService: Required by Spring Security to load user-specific data.
- loadUserByUsername(String username): Loads user details for authentication.
- saveUser(User user): Saves a new user with an encoded password.
- findByUsername(String username): Finds a user by username.
1.6 Create Security Configuration
Create a SecurityConfig class in the com.example.userservice.config package:
package com.example.userservice.config;
import com.example.userservice.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
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.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
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 UserService userService;
@Autowired
public SecurityConfig(UserService userService) {
this.userService = userService;
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(auth -> auth
.requestMatchers("/auth/**").permitAll()
.anyRequest().authenticated()
)
.formLogin(withDefaults());
return http.build();
}
@Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Explanation:
- @Configuration: Marks the class as a configuration component.
- @EnableWebSecurity: Enables Spring Security.
- securityFilterChain(HttpSecurity http): Configures HTTP security, disabling CSRF and permitting access to /auth/** endpoints.
- configure(AuthenticationManagerBuilder auth): Configures the authentication manager with user details service and password encoder.
- passwordEncoder(): Configures the password encoder.
1.7 Create Authentication Controller
Create an AuthController class in the com.example.userservice.controller package:
package com.example.userservice.controller;
import com.example.userservice.model.User;
import com.example.userservice.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;
@CrossOrigin(origins = "http://localhost:4200")
@RestController
@RequestMapping("/auth")
public class AuthController {
private final UserService userService;
@Autowired
public AuthController(UserService userService) {
this.userService = userService;
}
@PostMapping("/register")
public ResponseEntity registerUser(@RequestBody User
user) {
if (userService.findByUsername(user.getUsername()).isPresent()) {
return ResponseEntity.badRequest().build();
}
return ResponseEntity.ok(userService.saveUser(user));
}
@GetMapping("/login")
public ResponseEntity getCurrentUser() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String currentUsername = authentication.getName();
return userService.findByUsername(currentUsername)
.map(ResponseEntity::ok)
.orElseGet(() -> ResponseEntity.badRequest().build());
}
}
Explanation:
- @RestController: Marks the class as a REST controller.
- @RequestMapping("/auth"): Maps requests to /auth URL.
- registerUser(@RequestBody User user): Handles user registration.
- getCurrentUser(): Returns the current authenticated user.
- The @CrossOrigin annotation on the controller allows CORS requests from the specified origin (http://localhost:4200).
Step 2: Setting Up the Angular Frontend
2.1 Create an Angular Project
1.Open a terminal and run the following command to create a new Angular project:
ng new user-registration
2.ng new user-registration
cd user-registration
cd user-registration
cd user-registration
npm install bootstrap
Add Bootstrap to angular.json:
"styles": [
"src/styles.css",
"node_modules/bootstrap/dist/css/bootstrap.min.css"
],
2.3 Create Angular Services and Components
2.3.1 Create Auth Service
Generate the AuthService:
ng generate service services/auth
Edit auth.service.ts:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class AuthService {
private baseUrl = 'http://localhost:8080/auth';
constructor(private http: HttpClient) { }
register(user: any): Observable {
return this.http.post(`${this.baseUrl}/register`, user);
}
login(credentials: any): Observable {
return this.http.post(`${this.baseUrl}/login`, credentials);
}
}
Explanation:
@Injectable({ providedIn: 'root' }): Marks the service as injectable and available throughout the app.HttpClient: Service for making HTTP requests.register(user: any): Sends a POST request to register a new user.login(credentials: any): Sends a POST request to log in a user.
2.3.2 Create Registration Component
Generate the Registration component:
ng generate component components/register
Edit register.component.ts:
import { Component } from '@angular/core';
import { AuthService } from '../../services/auth.service';
import { Router } from '@angular/router';
@Component({
selector: 'app-register',
templateUrl: './register.component.html',
styleUrls: ['./register.component.css']
})
export class RegisterComponent {
user: any = {};
constructor(private authService: AuthService, private router: Router) { }
register() {
this.authService.register(this.user).subscribe(() => {
this.router.navigate(['/login']);
}, error => {
console.error('Registration error: ', error);
});
}
}
Edit register.component.html:
< div class="container mt-5">
< div class="row justify-content-center">
< div class="col-md-6">
< div class="card">
< div class="card-header">Registration< /div>
< div class="card-body">
< form (ngSubmit)="register()">
< div class="form-group">
< label for="username">Username< /label>
< input type="text" class="form-control" id="username" [(ngModel)]="user.username" name="username">
< /div>
< div class="form-group">
< label for="email">Email< /label>
< input type="email" class="form-control" id="email" [(ngModel)]="user.email" name="email">
< /div>
< div class="form-group">
< label for="password">Password< /label>
< input type="password" class="form-control" id="password" [(ngModel)]="user.password" name="password">
< /div>
< button type="submit" class="btn btn-primary">Register< /button>
< /form>
< /div>
< /div>
< /div>
< /div>
< /div>
Explanation:
- register(): Calls the AuthService to register a new user.
[(ngModel)]="user.username",[(ngModel)]="user.email",[(ngModel)]="user.password": Two-way data binding for form fields.
2.3.3 Create Login Component
Generate the Login component:
ng generate component components/login
Edit login.component.ts:
import { Component } from '@angular/core';
import { AuthService } from '../../services/auth.service';
import { Router } from '@angular/router';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent {
credentials: any = {};
constructor(private authService: AuthService, private router: Router) { }
login() {
this.authService.login(this.credentials).subscribe(() => {
this.router.navigate(['/home']);
}, error => {
console.error('Login error: ', error);
});
}
}
Edit login.component.html:
< div class="container mt-5">
< div class="row justify-content-center">
< div class="col-md-6">
< div class="card">
< div class="card-header">Login< /div>
< div class="card-body">
< form (ngSubmit)="login()">
< div class="form-group">
< label for="username">Username< /label>
< input type="text" class="form-control" id="username" [(ngModel)]="credentials.username" name="username">
< /div>
< div class="form-group">
< label for="password">Password< /label>
< input type="password" class="form-control" id="password" [(ngModel)]="credentials.password" name="password">
< /div>
< button type="submit" class="btn btn-primary">Login< /button>
< /form>
< /div>
< /div>
< /div>
< /div>
< /div>
Explanation:
- login(): Calls the AuthService to log in a user.
- [(ngModel)]="credentials.username", [(ngModel)]="credentials.password": Two-way data binding for form fields.
2.4 Update Angular Routing
Edit app-routing.module.ts:
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { RegisterComponent } from './components/register/register.component';
import { LoginComponent } from './components/login/login.component';
const routes: Routes = [
{ path: 'register', component: RegisterComponent },
{ path: 'login', component: LoginComponent },
{ path: '', redirectTo: '/register', pathMatch: 'full' }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
Explanation:
- Defines routes for the registration and login components.
- Redirects the root path to the registration component.
2.5 Update Angular App Module
Edit app.module.ts:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { RegisterComponent } from './components/register/register.component';
import { LoginComponent } from './components/login/login.component';
@NgModule({
declarations: [
AppComponent,
RegisterComponent,
LoginComponent
],
imports: [
BrowserModule,
AppRoutingModule,
FormsModule,
HttpClientModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Explanation:
- Imports necessary modules for the Angular app.
- Declares the components used in the app.
- Sets up the app's root module.
2.6 Run the Angular Application
Open a terminal in the Angular project directory and run the application:
ng serve
Visit http://localhost:4200 in your web browser to see the application.
Conclusion
In this tutorial, we created a Spring Boot backend and an Angular frontend to handle user
registration and login
functionalities. The backend handles user data and authentication
using Spring Security 6.1, while the frontend
provides a user-friendly interface using
Angular 17. By following this structure, you can extend and customize the
application as
needed.