Cloud computing

Introduction

Architecture de Spring Cloud

Eureka Config Serve Zuul Consul Hystrix Resilience4J

Spring Boot (BackEnd) TPs

Creation,Dépendance,Configuration Exemple Video RestController

Produit Rest API

Entity et repository Ajouter Afficher Liste Produit Détails,Supprimer,Vider Modifier

Angular (FrontEnd)

Angular Rappel CLient CRUD

Spring Security

User Auth

CRUD

Vente CRUD

To be Continued...

Middlewares Orientés Messages

Communication Synchrone vs. Asynchrone API JMS : Java Message Service JMS avec ActiveMQ et HornetQ KAFKA

Spring Batch

Spring Batch

Stream Processing

Kafka Streams

Architectures Serverless

Architectures Serverless Résumé



Spring Security

Authentication with JWT in Spring Boot, Angular, and MySQL

Step 1: Set Up Your Spring Boot Backend

1. Create a New Spring Boot Project

  • Go to Spring Initializr.
  • Select dependencies: Spring Web, Spring Security, Spring Data JPA, and MySQL Driver.
  • Generate and download the project, then unzip it.

2. Configure Your Application Properties

spring.datasource.url=jdbc:mysql://localhost:3306/your_database_name
spring.datasource.username=your_username
spring.datasource.password=your_password
spring.jpa.hibernate.ddl-auto=update
spring.security.user.name=admin
spring.security.user.password=admin

Key Modules and Techniques:

  1. Spring Security : A powerful security framework that provides authentication, authorization, and protection against common attacks (e.g., CSRF).
  2. JWT (JSON Web Token) : Used to securely transmit information between client and server as a JSON object. It is widely used for stateless authentication in RESTful services.
  3. Angular : We use Angular for the frontend where we implement the login form and manage user sessions using JWT.
  4. BCrypt : For securely hashing passwords before storing them in the database.
  5. Spring Boot : The framework that simplifies Spring application development, making it quick to build RESTful services.

pom.xml

Dependencies:We need specific dependencies for Spring Security, JWT, and password hashing (BCrypt). Below is the pom.xml file setup:


<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.3.4</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.emsi</groupId>
	<artifactId>serviceSecurity</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>serviceSecurity</name>
	<description>Demo project for Spring Boot</description>
	<url/>
	<licenses>
		<license/>
	</licenses>
	<developers>
		<developer/>
	</developers>
	<scm>
		<connection/>
		<developerConnection/>
		<tag/>
		<url/>
	</scm>
	<properties>
		<java.version>17</java.version>
	</properties>
	<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>


    <!-- JJWT for JWT handling -->
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-api</artifactId>
        <version>0.11.5</version>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-impl</artifactId>
        <version>0.11.5</version>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-jackson</artifactId>
        <version>0.11.5</version>
    </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.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>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.11.5</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.11.5</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.11.5</version>
</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>


These dependencies enable us to use Spring Security, JWT, and hashing techniques for password protection.

User Entity

The User entity represents users in the system. We include a password field for authentication.
package com.emsi.serviceSecurity.models;

import jakarta.persistence.*;

@Entity
@Table(name = "users") // Optional: specify the table name if needed
public class User {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(nullable = false, unique = true) // Ensures username is unique and not null
    private String username;
    
    @Column(nullable = false) // Ensures password is not null
    private String password;

    // Default constructor
    public User() {
    }

    // 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;
    }
}

UserRepository


package com.emsi.serviceSecurity.repositories;

import org.springframework.data.jpa.repository.JpaRepository;

import com.emsi.serviceSecurity.models.User;

public interface UserRepository extends JpaRepository {
    User findByUsername(String username);
}

JWT Utility Class

  • generateToken : Creates a token based on the user's email.
  • validateToken : Ensures that the token is valid and not expired.
  • extractUsername : Retrieves the username from the token.
generating and validating JWT tokens.
/*
 * The JwtUtil class is responsible for handling JSON Web Tokens (JWT) in your application. It provides methods for generating, extracting information from, and validating JWTs. This utility class is essential for implementing authentication and securing API endpoints using JWTs.
 */

package com.emsi.serviceSecurity.utils;

// Import necessary classes from the JWT library and Java utilities.
import io.jsonwebtoken.*;
import org.springframework.stereotype.Component;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;

import java.security.Key;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

// The @Component annotation indicates that this class is a Spring-managed bean.
@Component
public class JwtUtil {
	
    // Generate a secure key for signing the JWT using the HS256 algorithm.
    private static final Key SECRET_KEY = Keys.secretKeyFor(SignatureAlgorithm.HS256); // Secure key for signing JWTs

    // Define the expiration time for the token (1 hour in milliseconds).
    private final long EXPIRATION_TIME = 1000 * 60 * 60; // 1 hour

    // Generate a token for the given username.
    public String generateToken(String username) {
        // Create an empty claims map for additional information if needed.
        Map claims = new HashMap<>();
        // Call the createToken method to create a JWT with the provided claims and username.
        return createToken(claims, username);
    }

    // Create a JWT using claims and a subject (username).
    private String createToken(Map claims, String subject) {
        // Build the JWT using the Jwts builder with claims, subject, issued date, expiration date, and signing key.
        return Jwts.builder()
                .setClaims(claims) // Set additional claims (if any)
                .setSubject(subject) // Set the subject (username)
                .setIssuedAt(new Date(System.currentTimeMillis())) // Set the token issue date
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME)) // Set expiration date
                .signWith(SignatureAlgorithm.HS256, SECRET_KEY) // Sign the token with the secret key
                .compact(); // Compact the JWT into a string
    }

    // Extract the username from the provided token.
    public String extractUsername(String token) {
        // Call the method to extract all claims from the token and get the subject.
        return extractAllClaims(token).getSubject();
    }

    // Extract all claims from the provided token.
    private Claims extractAllClaims(String token) {
        // Parse the token using the signing key and return the claims body.
        return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();
    }

    // Validate the token by checking the extracted username and whether the token is expired.
    public boolean validateToken(String token, String username) {
        // Extract the username from the token.
        final String extractedUsername = extractUsername(token);
        // Check if the extracted username matches the provided username and if the token is not expired.
        return (extractedUsername.equals(username) && !isTokenExpired(token));
    }

    // Check if the token is expired.
    private boolean isTokenExpired(String token) {
        // Get the expiration date from the token claims and compare it with the current date.
        return extractAllClaims(token).getExpiration().before(new Date());
    }
}

JwtRequestFilter


/*Its primary purpose is to intercept incoming HTTP requests to check for a JWT (JSON Web Token) in the Authorization header. If a valid JWT is found, it extracts the username from the token, retrieves the corresponding user details, and sets the authentication in the Spring Security context, allowing the user to access protected resources.
 * */
 
package com.emsi.serviceSecurity.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import com.emsi.serviceSecurity.utils.JwtUtil;

import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
//This annotation indicates that this class is a Spring component, allowing it to be 
//automatically detected and managed by Spring's application context.
@Component
public class JwtRequestFilter extends OncePerRequestFilter {

 // Automatically inject the JwtUtil class, which provides utility methods for 
 // working with JWTs, such as extracting information and validating tokens.
 @Autowired
 private JwtUtil jwtUtil;

 // Automatically inject the UserDetailsService, which is used to retrieve user 
 // details from the database based on the username.
 @Autowired
 private UserDetailsService userDetailsService;

 // The doFilterInternal method is overridden to perform filtering logic on each 
 // incoming HTTP request.
 @Override
 protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
         throws ServletException, IOException {
     
     // Retrieve the "Authorization" header from the incoming request.
     final String authorizationHeader = request.getHeader("Authorization");

     // Initialize variables to hold the username and JWT token.
     String username = null;
     String jwt = null;

     // Check if the Authorization header is present and starts with "Bearer ".
     // This is the standard format for passing JWTs.
     if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
         // Extract the JWT token from the header (removing the "Bearer " prefix).
         jwt = authorizationHeader.substring(7); 
         // Use the JwtUtil class to extract the username from the JWT.
         username = jwtUtil.extractUsername(jwt); 
     }

     // If a username is extracted and there is no existing authentication in the context,
     // proceed to validate the token and set the authentication.
     if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
         // Load user details from the database using the username.
         UserDetails userDetails = userDetailsService.loadUserByUsername(username);
         // Validate the token to ensure it is still valid for the user.
         if (jwtUtil.validateToken(jwt, userDetails.getUsername())) {
             // If the token is valid, create an authentication object.
             var authentication = new UsernamePasswordAuthenticationToken(
                     userDetails, null, userDetails.getAuthorities());
             // Set additional details in the authentication object (e.g., IP address).
             authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
             // Set the authentication object in the SecurityContext, allowing 
             // Spring Security to recognize the user as authenticated.
             SecurityContextHolder.getContext().setAuthentication(authentication);
         }
     }

     // Continue the filter chain, allowing the request to proceed to the next filter 
     // or the requested resource.
     chain.doFilter(request, response); 
 }
}

SecurityConfig



/*
 *  It sets up the security filters, authentication management, and access rules for the application. By extending WebSecurityConfigurerAdapter, it allows customization of the security settings. In this class, you define how the application should handle authentication, which endpoints are public, and how JWT authentication is processed.
 */


package com.emsi.serviceSecurity.config;

// Import necessary Spring Security and Servlet classes for configuration.
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.http.SessionCreationPolicy;
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;

import com.emsi.serviceSecurity.services.CustomUserDetailsService;

import jakarta.servlet.http.HttpServletResponse;

// The @Configuration annotation indicates that this class is a source of bean definitions.
// The @EnableWebSecurity annotation enables Spring Security’s web security support.
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    // Automatically inject the custom UserDetailsService for loading user-specific data.
    @Autowired
    private CustomUserDetailsService userDetailsService;

    // Automatically inject the JwtRequestFilter to handle JWT validation for incoming requests.
    @Autowired
    private JwtRequestFilter jwtRequestFilter;

    // This bean provides a password encoder that will be used to encode passwords.
    @Bean
    public PasswordEncoder passwordEncoder() {
        // Return a new instance of BCryptPasswordEncoder, a strong password hashing algorithm.
        return new BCryptPasswordEncoder();
    }

    // This bean defines the security filter chain, which contains the security configurations.
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        // Disable CSRF protection as it is not required for stateless APIs using JWT.
        http.csrf().disable()
            // Configure which requests are authorized.
            .authorizeRequests()
            .requestMatchers("/auth/login", "/auth/register").permitAll() // Allow public access to login and register endpoints.
            .anyRequest().authenticated() // Require authentication for all other requests.
            .and()
            // Configure exception handling for unauthorized requests.
            .exceptionHandling()
            .authenticationEntryPoint((request, response, authException) -> {
                // Send an HTTP 401 Unauthorized response when authentication fails.
                response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
            })
            .and()
            // Configure session management to be stateless, as we are using JWT for authentication.
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        // Add the JWT request filter before the UsernamePasswordAuthenticationFilter to handle JWT validation.
        http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);

        // Build and return the security filter chain.
        return http.build();
    }

    // This bean provides an AuthenticationManager for managing authentication.
    @Bean
    public AuthenticationManager authManager(HttpSecurity http) throws Exception {
        // Get the shared AuthenticationManagerBuilder from the HttpSecurity context.
        AuthenticationManagerBuilder authenticationManagerBuilder = 
                http.getSharedObject(AuthenticationManagerBuilder.class);
        
        // Set the custom UserDetailsService and the password encoder for the authentication manager.
        authenticationManagerBuilder
                .userDetailsService(userDetailsService) // Use the custom UserDetailsService for user data retrieval.
                .passwordEncoder(passwordEncoder()); // Use the BCrypt password encoder for password matching.

        // Build and return the AuthenticationManager.
        return authenticationManagerBuilder.build();
    }
}

AuthController Controller

This REST controller exposes the login API, handles login requests, and returns the JWT token if the credentials are valid.
/*The AuthController class is a Spring REST controller responsible for handling user authentication-related operations such as registration, login, and logout. It utilizes JWT for authentication and BCrypt for password hashing to ensure secure handling of user credentials.
 * */
 

package com.emsi.serviceSecurity.controllers;

import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired; // Allows for dependency injection
import org.springframework.http.HttpStatus; // Represents HTTP status codes
import org.springframework.http.ResponseEntity; // Used to return HTTP responses
import org.springframework.security.crypto.password.PasswordEncoder; // Interface for password encoding
import org.springframework.web.bind.annotation.*; // Contains annotations for RESTful web services

import com.emsi.serviceSecurity.models.User; // User model class
import com.emsi.serviceSecurity.repositories.UserRepository; // Repository for User data access
import com.emsi.serviceSecurity.utils.JwtUtil; // Utility class for JWT operations

@CrossOrigin(origins = "http://localhost:63828") // Allows requests from specified origin
@RestController // Indicates that this class is a REST controller
@RequestMapping("/auth") // Maps all requests under /auth to this controller
public class AuthController {

    @Autowired
    private UserRepository userRepository; // Injected repository for accessing user data

    @Autowired
    private JwtUtil jwtUtil; // Injected JWT utility for token generation and validation

    private final PasswordEncoder passwordEncoder; // Inject PasswordEncoder for hashing passwords

    // Constructor to initialize userRepository and passwordEncoder
    public AuthController(UserRepository userRepository, PasswordEncoder passwordEncoder) {
        this.userRepository = userRepository; // Assign UserRepository
        this.passwordEncoder = passwordEncoder; // Assign PasswordEncoder
    }

    // Handles user registration
    @PostMapping("/register") // Maps POST requests to /auth/register
    public ResponseEntity> register(@RequestBody User user) {
        // Check if the username already exists in the database
        if (userRepository.findByUsername(user.getUsername()) != null) {
            // Return a bad request response if username exists
            return ResponseEntity.badRequest().body(Map.of("message", "Username already exists!"));
        }

        // Hash the password before saving to the database
        user.setPassword(passwordEncoder.encode(user.getPassword()));

        // Save the new user to the database
        userRepository.save(user);
        // Return a response indicating successful registration
        return ResponseEntity.status(HttpStatus.CREATED).body(Map.of("message", "User registered successfully!"));
    }

    // Handles user login
    @PostMapping("/login") // Maps POST requests to /auth/login
    public ResponseEntity login(@RequestBody User user) {
        // Retrieve the user from the database using the provided username
        User foundUser = userRepository.findByUsername(user.getUsername());

        // Check if the user was found
        if (foundUser == null) {
            System.out.println("User not found: " + user.getUsername()); // Log for debugging
            // Return a response indicating the user was not found
            return new ResponseEntity<>("User not found", HttpStatus.UNAUTHORIZED);
        }

        // Check if the provided password matches the stored hashed password
        boolean passwordMatches = passwordEncoder.matches(user.getPassword(), foundUser.getPassword());
        System.out.println("Password matches: " + passwordMatches); // Log for debugging

        // If passwords match, generate a JWT token
        if (passwordMatches) {
            String token = jwtUtil.generateToken(foundUser.getUsername()); // Generate JWT token
            return new ResponseEntity<>(token, HttpStatus.OK); // Return the token
        } else {
            // If passwords don't match, return "Invalid credentials"
            return new ResponseEntity<>("Invalid credentials test", HttpStatus.UNAUTHORIZED);
        }
    }

    // Handles user logout
    @PostMapping("/logout") // Maps POST requests to /auth/logout
    public ResponseEntity logout() {
        // In stateless JWT authentication, logout can be handled by deleting the token client-side
        return new ResponseEntity<>("User logged out successfully!", HttpStatus.OK); // Confirm logout
    }

    // A simple hello endpoint to confirm authentication
    @GetMapping("/hello") // Maps GET requests to /auth/hello
    public ResponseEntity hello() {
        // Return a response indicating successful authentication
        return new ResponseEntity<>("Hello! You are authenticated.", HttpStatus.OK);
    }
}

Angular

AuthService Service for Authentication


The AuthService handles login, storing tokens, and checking authentication status.import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  private baseUrl = 'http://localhost:8080/auth'; // Base URL for your backend API
  private jwtToken: string | null = null; // Store JWT token

  constructor(private http: HttpClient) {}

  register(user: any): Observable {
    return this.http.post(`${this.baseUrl}/register`, user).pipe(
      catchError((error) => {
        // Handle registration error
        console.error('Registration error:', error);
        return throwError(() => new Error(error.error?.message || 'Registration failed'));
      })
    );
  }

  login(user: any): Observable {
    // Specify responseType as 'text' to avoid JSON parsing errors
    return this.http.post(`${this.baseUrl}/login`, user, { responseType: 'text' as 'json' }).pipe(
      catchError((error) => {
        // Handle login error
        console.error('Login error:', error);
        return throwError(() => new Error(error.error?.message || 'Login failed'));
      })
    );
  }

  setToken(token: string): void {
    this.jwtToken = token; // Store the JWT token
    localStorage.setItem('token', token); // Optionally store it in local storage
  }

  getToken(): string | null {
    return this.jwtToken || localStorage.getItem('token'); // Retrieve the token
  }

  logout(): void {
    this.jwtToken = null; // Clear the stored token
    localStorage.removeItem('token'); // Remove it from local storage
    console.log('User logged out successfully!');
  }

  isLoggedIn(): boolean {
    const token = this.getToken();
    // Check if the token exists and is not expired (you can implement additional checks here)
    return !!token;
  }
}


RegisterComponent.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 = { username: '', password: '' };
  registrationError: string | null = null;
  registrationSuccess: string | null = null;

  constructor(private authService: AuthService, private router: Router) {}

  register() {
    this.authService.register(this.user).subscribe(
      response => {
        this.registrationSuccess = response.message;
        console.log(this.registrationSuccess);
        this.registrationError = null;
       


        /*setTimeout(() => {
          this.router.navigate(['/login']);
        }, 2000);
        */
      },
      error => {
        this.registrationError = error.error.message || 'Registration failed. Please try again.';
     
       
      }
    );
  }
}

RegisterComponent HTML

<div class="register-container">
    <h2>Register</h2>
    <form (ngSubmit)="register()">
        <div class="form-group">
            <label for="username">Username</label>
            <input type="text" id="username" [(ngModel)]="user.username" name="username" required />
        </div>
        <div class="form-group">
            <label for="password">Password</label>
            <input type="password" id="password" [(ngModel)]="user.password" name="password" required />
        </div>
        <button type="submit">Register</button>
    </form>
    <div *ngIf="registrationError" class="error">{{ registrationError }}</div>
    <div *ngIf="registrationSuccess" class="success">{{ registrationSuccess }}</div>
</div>

LoginComponent.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 {
  user = { username: '', password: '' };
  loginError: string | null = null; // Declare loginError property

  constructor(private authService: AuthService, private router: Router) {}

  login() {
    this.authService.login(this.user).subscribe(
      token => {
        localStorage.setItem('token', token); // Store JWT token
        this.loginError = null; // Clear any previous error
        this.router.navigate(['/']); // Redirect to home or another page
        this.loginError = 'ok';
      },
      error => {
        this.loginError = error.error || 'Invalid credentials'; // Set error message
      }
    );
  }
}

LoginComponent HTML

<div class="login-container">
    <h2>Login</h2>
    <form (ngSubmit)="login()">
        <div class="form-group">
            <label for="username">Username</label>
            <input type="text" id="username" [(ngModel)]="user.username" name="username" required />
        </div>
        <div class="form-group">
            <label for="password">Password</label>
            <input type="password" id="password" [(ngModel)]="user.password" name="password" required />
        </div>
        <button type="submit">Login</button>
    </form>
    <div *ngIf="loginError" class="error">{{ loginError }}</div>
</div>

AuthGuard (Route Guard)

The AuthGuard protects certain routes by checking if the user is authenticated.

import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';
import { AuthService } from './auth.service';

@Injectable({
  providedIn: 'root'
})
export class AuthGuard implements CanActivate {

  constructor(private authService: AuthService, private router: Router) { }

  // This function determines whether the route can be activated
  canActivate(): boolean {
    if (this.authService.isAuthenticated()) {
      return true; // Allow access to the route if the user is authenticated
    } else {
      this.router.navigate(['/login']); // Redirect to login if not authenticated
      return false;
    }
  }
}

App.module.ts )


import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { FormsModule } from '@angular/forms'; // Import FormsModule


import { HttpClientModule } from '@angular/common/http';

import { LoginComponent } from './components/login/login.component';
import { RegisterComponent } from './components/register/register.component';

@NgModule({
  declarations: [
    AppComponent,
    LoginComponent,
    RegisterComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule,
    FormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

AppRoutingModule (Routing Configuration)




import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { LoginComponent } from './components/login/login.component';
import { RegisterComponent } from './components/register/register.component';
import { AuthGuard } from './auth.guard';

const routes: Routes = [
  { path: 'login', component: LoginComponent },
    { path: 'register', component: RegisterComponent },
	  { path: 'hello', component: HelloComponent, canActivate: [AuthGuard] }, // Protect the route

    { path: '', redirectTo: '/login', pathMatch: 'full' }, // Default route
    { path: '**', redirectTo: '/login' } // Wildcard route for 404

];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

  1. AuthService handles authentication by interacting with the backend and storing JWT tokens.
  2. LoginComponent displays a login form and processes the login request.
  3. HelloComponent is a protected component that welcomes authenticated users.
  4. AuthGuard protects routes, ensuring only authenticated users can access them.
  5. The CSS provides a simple yet elegant design to enhance user experience.