Angular 14 Spring Boot JWT Authentication example | Angular 14 + Spring Security + MySQL Full Stack – Part 3: Build Frontend

The tutorial is Part 3 of the series: Angular Spring Boot JWT Authentication example | Angular 14 + Spring Security + MySQL Full Stack. Today we’re gonna build a Angular HTTP Client App that can interact with SpringBoot JWT Authentication Server.

Part 1: Overview and Architecture.
Part 2: Build SpringBoot Backend


Related Posts:
Angular 14 Http Interceptor – with Node.js RestAPIs
Angular 14 HttpClient – Get/Post/Put/Delete requests + SpringBoot RestAPIs + Bootstrap 4

How to build JWT Authentication frontend with Angular

Demo

Send Requests to Server

Add Token to Header with HttpInterceptor

We use Angular HttpInterceptor with intercept() method to inspect and transform HTTP requests (before they are sent to server):

import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http';

const TOKEN_HEADER_KEY = 'Authorization';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
    ...
    intercept(req: HttpRequest, next: HttpHandler) {
        let authReq = req;
        const token = ...;
        if (token != null) {
            authReq = req.clone({ headers: req.headers.set(TOKEN_HEADER_KEY, 'Bearer ' + token) });
        }
        return next.handle(authReq);
    }
}

export const httpInterceptorProviders = [
    { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }
];

– The HTTPRequest object will be inspected and forwarded to handle() method of the HttpHandler object.
handle() method transforms HTTPRequest object into an Observable of HttpEvents which includes the server’s response.

What is next: HttpHandler object?
-> This object represents the next interceptor in the chain of interceptors. The final ‘next’ in the chain is the Angular HttpClient handler.

HTTP Request using HttpClient

We send HTTP Requests (signin/signup) using Angular HttpClient:

import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';

const httpOptions = {
  headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};

export class AuthService {

  private loginUrl = 'http://localhost:8080/api/auth/signin';
  private signupUrl = 'http://localhost:8080/api/auth/signup';

  constructor(private http: HttpClient) {
  }

  // JwtResponse(accessToken,type,username,authorities)
  attemptAuth(credentials: AuthLoginInfo): Observable {
    return this.http.post(this.loginUrl, credentials, httpOptions);
  }

  // SignUpInfo(name,username,email,role,password)
  signUp(info: SignUpInfo): Observable {
    return this.http.post(this.signupUrl, info, httpOptions);
  }
}

AuthLoginInfo fields & SignUpInfo fields are validated using Angular template-driven Form.

Once Token is saved, we can access protected resources simply:

export class UserService {
  private userUrl = 'http://localhost:8080/api/test/user';
  private pmUrl = 'http://localhost:8080/api/test/pm';
  private adminUrl = 'http://localhost:8080/api/test/admin';

  constructor(private http: HttpClient) { }

  getUserBoard(): Observable {
    return this.http.get(this.userUrl, { responseType: 'text' });
  }

  getPMBoard(): Observable {
    return this.http.get(this.pmUrl, { responseType: 'text' });
  }

  getAdminBoard(): Observable {
    return this.http.get(this.adminUrl, { responseType: 'text' });
  }
}

Handle Responses

Using AuthService to work with Observable object:

Signup Response

export class RegisterComponent implements OnInit {
  ...
  constructor(private authService: AuthService) { }

  onSubmit() {
    ...
    this.authService.signUp(this.signupInfo).subscribe(
      data => {
        ...
      },
      error => {
        ...
      }
    );
  }
}

Login Response

In addition to using AuthService to work with Observable object, we also call TokenStorageService methods to save Token, Username, Authorities:

export class LoginComponent implements OnInit {
  ...
  constructor(private authService: AuthService, private tokenStorage: TokenStorageService) { }

  ngOnInit() {
    if (this.tokenStorage.getToken()) {
      this.isLoggedIn = true;
      this.roles = this.tokenStorage.getAuthorities();
    }
  }

  onSubmit() {
    ...
    this.authService.attemptAuth(this.loginInfo).subscribe(
      data => {
        this.tokenStorage.saveToken(data.accessToken);
        this.tokenStorage.saveUsername(data.username);
        this.tokenStorage.saveAuthorities(data.authorities);

        this.roles = this.tokenStorage.getAuthorities();
        ...
        reloadPage();
      },
      error => {
        ...
      }
    );
  }
}

Manage Token & User Logout

We use TokenStorageService to manage Token inside Browser’s sessionStorage:

export class TokenStorageService {
  private roles: Array = [];

  public saveToken(token: string) {
    window.sessionStorage.removeItem(TOKEN_KEY);
    window.sessionStorage.setItem(TOKEN_KEY, token);
  }

  public getToken(): string {
    return sessionStorage.getItem(TOKEN_KEY);
  }

  public saveUsername(username: string) {
    window.sessionStorage.removeItem(USERNAME_KEY);
    window.sessionStorage.setItem(USERNAME_KEY, username);
  }

  public getUsername(): string {
    return sessionStorage.getItem(USERNAME_KEY);
  }

  public saveAuthorities(authorities: string[]) {
    window.sessionStorage.removeItem(AUTHORITIES_KEY);
    window.sessionStorage.setItem(AUTHORITIES_KEY, JSON.stringify(authorities));
  }

  public getAuthorities(): string[] {
    this.roles = [];

    if (sessionStorage.getItem(TOKEN_KEY)) {
      JSON.parse(sessionStorage.getItem(AUTHORITIES_KEY)).forEach(authority => {
        this.roles.push(authority.authority);
      });
    }

    return this.roles;
  }
}

For Logout action, we only need to clear Browser’s sessionStorage:

export class TokenStorageService {
  ...
  signOut() {
    window.sessionStorage.clear();
  }
}

Angular Client for JWT Authentication Overview

Goal

We will build Angular Client which allows users to register, login account. And depending on the role of current User (user, pm or admin), this system accepts what he can access:

spring-boot-angular-spring-security-jwt-authentication-demo-goal

The diagram below show how our system handles User Registration and User Login processes:

angular-spring-security-jwt-authentication-work-process-diagram

Elements Overview

spring-boot-angular-spring-security-jwt-authentication-architecture-diagram-front-end-client

app.component is the parent component that contains routerLink and router-outlet for routing. It also has an authority variable as the condition for displaying items on navigation bar.
user.component, pm.component, admin.component correspond to Angular Components for User Board, PM Board, Admin Board. Each Board uses user.service to access authority data.
register.component contains User Registration form, submission of the form will call auth.service.
login.component contains User Login form, submission of the form will call auth.service and token-storage.service.

user.service gets access to authority data from Server using Angular HttpClient ($http service).
auth.service handles authentication and signup actions with Server using Angular HttpClient ($http service).
– every HTTP request by $http service will be inspected and transformed before being sent to the Server by auth-interceptor (implements HttpInterceptor).
auth-interceptor check and get Token from token-storage.service to add the Token to Authorization Header of the HTTP Requests.

token-storage.service manages Token inside Browser’s sessionStorage.

Technologies

– Angular 14
– RxJS 6

Project Structure

spring-boot-angular-spring-security-jwt-authentication-front-end-angular-project-structure

Practice

Create Services & Components

Run commands below:
ng g s auth/auth
ng g s auth/token-storage
ng g s services/user

ng g c login
ng g c register
ng g c home
ng g c user
ng g c pm
ng g c admin

AppModule

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { HttpClientModule } from '@angular/common/http';
import { FormsModule } from '@angular/forms';

import { AppComponent } from './app.component';
import { LoginComponent } from './login/login.component';
import { UserComponent } from './user/user.component';
import { RegisterComponent } from './register/register.component';
import { HomeComponent } from './home/home.component';
import { AdminComponent } from './admin/admin.component';
import { PmComponent } from './pm/pm.component';

import { httpInterceptorProviders } from './auth/auth-interceptor';

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

Services

Auth Models

auth/login-info.ts

export class AuthLoginInfo {
    username: string;
    password: string;

    constructor(username: string, password: string) {
        this.username = username;
        this.password = password;
    }
}

auth/sigup-info.ts

export class SignUpInfo {
    name: string;
    username: string;
    email: string;
    role: string[];
    password: string;

    constructor(name: string, username: string, email: string, password: string) {
        this.name = name;
        this.username = username;
        this.email = email;
        this.password = password;
        this.role = ['user'];
    }
}

auth/jwt-response.ts

export class JwtResponse {
    accessToken: string;
    type: string;
    username: string;
    authorities: string[];
}

Auth Service

auth/auth.service.ts

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';

import { JwtResponse } from './jwt-response';
import { AuthLoginInfo } from './login-info';
import { SignUpInfo } from './signup-info';

const httpOptions = {
  headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};

@Injectable({
  providedIn: 'root'
})
export class AuthService {

  private loginUrl = 'http://localhost:8080/api/auth/signin';
  private signupUrl = 'http://localhost:8080/api/auth/signup';

  constructor(private http: HttpClient) {
  }

  attemptAuth(credentials: AuthLoginInfo): Observable {
    return this.http.post(this.loginUrl, credentials, httpOptions);
  }

  signUp(info: SignUpInfo): Observable {
    return this.http.post(this.signupUrl, info, httpOptions);
  }
}

Token Storage Service

auth/token-storage.service.ts

import { Injectable } from '@angular/core';

const TOKEN_KEY = 'AuthToken';
const USERNAME_KEY = 'AuthUsername';
const AUTHORITIES_KEY = 'AuthAuthorities';

@Injectable({
  providedIn: 'root'
})
export class TokenStorageService {
  private roles: Array = [];
  constructor() { }

  signOut() {
    window.sessionStorage.clear();
  }

  public saveToken(token: string) {
    window.sessionStorage.removeItem(TOKEN_KEY);
    window.sessionStorage.setItem(TOKEN_KEY, token);
  }

  public getToken(): string {
    return sessionStorage.getItem(TOKEN_KEY);
  }

  public saveUsername(username: string) {
    window.sessionStorage.removeItem(USERNAME_KEY);
    window.sessionStorage.setItem(USERNAME_KEY, username);
  }

  public getUsername(): string {
    return sessionStorage.getItem(USERNAME_KEY);
  }

  public saveAuthorities(authorities: string[]) {
    window.sessionStorage.removeItem(AUTHORITIES_KEY);
    window.sessionStorage.setItem(AUTHORITIES_KEY, JSON.stringify(authorities));
  }

  public getAuthorities(): string[] {
    this.roles = [];

    if (sessionStorage.getItem(TOKEN_KEY)) {
      JSON.parse(sessionStorage.getItem(AUTHORITIES_KEY)).forEach(authority => {
        this.roles.push(authority.authority);
      });
    }

    return this.roles;
  }
}

User Service

services/user.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class UserService {

  private userUrl = 'http://localhost:8080/api/test/user';
  private pmUrl = 'http://localhost:8080/api/test/pm';
  private adminUrl = 'http://localhost:8080/api/test/admin';

  constructor(private http: HttpClient) { }

  getUserBoard(): Observable {
    return this.http.get(this.userUrl, { responseType: 'text' });
  }

  getPMBoard(): Observable {
    return this.http.get(this.pmUrl, { responseType: 'text' });
  }

  getAdminBoard(): Observable {
    return this.http.get(this.adminUrl, { responseType: 'text' });
  }
}

HTTP Interceptor

auth/auth-interceptor.ts

import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http';

import { TokenStorageService } from './token-storage.service';

const TOKEN_HEADER_KEY = 'Authorization';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

    constructor(private token: TokenStorageService) { }

    intercept(req: HttpRequest, next: HttpHandler) {
        let authReq = req;
        const token = this.token.getToken();
        if (token != null) {
            authReq = req.clone({ headers: req.headers.set(TOKEN_HEADER_KEY, 'Bearer ' + token) });
        }
        return next.handle(authReq);
    }
}

export const httpInterceptorProviders = [
    { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }
];

Components

Home Component

home.component.ts

import { Component, OnInit } from '@angular/core';

import { TokenStorageService } from '../auth/token-storage.service';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
  info: any;

  constructor(private token: TokenStorageService) { }

  ngOnInit() {
    this.info = {
      token: this.token.getToken(),
      username: this.token.getUsername(),
      authorities: this.token.getAuthorities()
    };
  }

  logout() {
    this.token.signOut();
    window.location.reload();
  }
}

home.component.html

<div *ngIf="info.token; else loggedOut">
  <h5 class="text-primary">Your Information</h5>
  <p>
    <strong>Username:</strong> {{info.username}}<br/>
    <strong>Roles:</strong> {{info.authorities}}<br />
    <strong>Token:</strong> {{info.token}}.
  </p>
  <button class="btn btn-secondary" (click)="logout()">Logout</button>
</div>

<ng-template #loggedOut>
  Please login.
</ng-template>

Login Component

login.component.ts

import { Component, OnInit } from '@angular/core';

import { AuthService } from '../auth/auth.service';
import { TokenStorageService } from '../auth/token-storage.service';
import { AuthLoginInfo } from '../auth/login-info';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
  form: any = {};
  isLoggedIn = false;
  isLoginFailed = false;
  errorMessage = '';
  roles: string[] = [];
  private loginInfo: AuthLoginInfo;

  constructor(private authService: AuthService, private tokenStorage: TokenStorageService) { }

  ngOnInit() {
    if (this.tokenStorage.getToken()) {
      this.isLoggedIn = true;
      this.roles = this.tokenStorage.getAuthorities();
    }
  }

  onSubmit() {
    console.log(this.form);

    this.loginInfo = new AuthLoginInfo(
      this.form.username,
      this.form.password);

    this.authService.attemptAuth(this.loginInfo).subscribe(
      data => {
        this.tokenStorage.saveToken(data.accessToken);
        this.tokenStorage.saveUsername(data.username);
        this.tokenStorage.saveAuthorities(data.authorities);

        this.isLoginFailed = false;
        this.isLoggedIn = true;
        this.roles = this.tokenStorage.getAuthorities();
        this.reloadPage();
      },
      error => {
        console.log(error);
        this.errorMessage = error.error.message;
        this.isLoginFailed = true;
      }
    );
  }

  reloadPage() {
    window.location.reload();
  }
}

login.component.html

<div *ngIf="isLoggedIn; else loggedOut">
  Logged in as {{roles}}.
</div>

<ng-template #loggedOut>
  <div class="row col-sm-6" style="max-width:350px;">
    <form name="form" (ngSubmit)="f.form.valid && onSubmit()" #f="ngForm" novalidate>
      <div class="form-group">
        <label for="username">Username</label>
        <input type="text" class="form-control" name="username" [(ngModel)]="form.username" #username="ngModel"
          required />
        <div *ngIf="f.submitted && username.invalid">
          <div *ngIf="username.errors.required">Username is required</div>
        </div>
      </div>
      <div class="form-group">
        <label for="password">Password</label>
        <input type="password" class="form-control" name="password" [(ngModel)]="form.password" #password="ngModel"
          required minlength="6" />
        <div *ngIf="f.submitted && password.invalid">
          <div *ngIf="password.errors.required">Password is required</div>
          <div *ngIf="password.errors.minlength">Password must be at least 6 characters</div>
        </div>
      </div>
      <div class="form-group">
        <button class="btn btn-primary">Login</button>
        <div *ngIf="f.submitted && isLoginFailed" class="alert alert-danger">
          Login failed: {{errorMessage}}
        </div>
      </div>
    </form>
    <hr />
    <p>Don't have an account?</p>
    <a href="signup" class="btn btn-success">Sign Up</a>
  </div>
</ng-template>

Register Component

register.component.ts

import { Component, OnInit } from '@angular/core';

import { AuthService } from '../auth/auth.service';
import { SignUpInfo } from '../auth/signup-info';

@Component({
  selector: 'app-register',
  templateUrl: './register.component.html',
  styleUrls: ['./register.component.css']
})
export class RegisterComponent implements OnInit {
  form: any = {};
  signupInfo: SignUpInfo;
  isSignedUp = false;
  isSignUpFailed = false;
  errorMessage = '';

  constructor(private authService: AuthService) { }

  ngOnInit() { }

  onSubmit() {
    console.log(this.form);

    this.signupInfo = new SignUpInfo(
      this.form.name,
      this.form.username,
      this.form.email,
      this.form.password);

    this.authService.signUp(this.signupInfo).subscribe(
      data => {
        console.log(data);
        this.isSignedUp = true;
        this.isSignUpFailed = false;
      },
      error => {
        console.log(error);
        this.errorMessage = error.error.message;
        this.isSignUpFailed = true;
      }
    );
  }
}

register.component.html

<div *ngIf="isSignedUp; else signupForm">
  Your registration is successful. Please login!
</div>

<ng-template #signupForm>
  <div class="row col-sm-6" style="max-width:350px;">
    <form name="form" (ngSubmit)="f.form.valid && onSubmit()" #f="ngForm" novalidate>
      <div class="form-group">
        <label for="name">Your name</label>
        <input type="text" class="form-control" name="name" [(ngModel)]="form.name" #name="ngModel" required />
        <div *ngIf="f.submitted && name.invalid">
          <div *ngIf="name.errors.required">Name is required</div>
        </div>
      </div>
      <div class="form-group">
        <label for="username">Username</label>
        <input type="text" class="form-control" name="username" [(ngModel)]="form.username" #username="ngModel"
          required />
        <div *ngIf="f.submitted && username.invalid">
          <div *ngIf="username.errors.required">Username is required</div>
        </div>
      </div>
      <div class="form-group">
        <label for="email">Email</label>
        <input type="text" class="form-control" name="email" [(ngModel)]="form.email" #email="ngModel" required email />
        <div *ngIf="f.submitted && email.invalid">
          <div *ngIf="email.errors.required">Email is required</div>
          <div *ngIf="email.errors.email">Email must be a valid email address</div>
        </div>
      </div>
      <div class="form-group">
        <label for="password">Password</label>
        <input type="password" class="form-control" name="password" [(ngModel)]="form.password" #password="ngModel"
          required minlength="6" />
        <div *ngIf="f.submitted && password.invalid">
          <div *ngIf="password.errors.required">Password is required</div>
          <div *ngIf="password.errors.minlength">Password must be at least 6 characters</div>
        </div>
      </div>
      <div class="form-group">
        <button class="btn btn-primary">Register</button>
        <div *ngIf="f.submitted && isSignUpFailed" class="alert alert-warning">
          Signup failed!<br/>{{errorMessage}}
        </div>
      </div>
    </form>
  </div>
</ng-template>

[UserRole] Components

user/pm/admin.component.html

<h4>Content from Server</h4>
{{board}}
{{errorMessage}}
</pre>
<code>user.component.ts</code>
<pre class="lang:java">
import { Component, OnInit } from '@angular/core';
import { UserService } from '../services/user.service';

@Component({
  selector: 'app-user',
  templateUrl: './user.component.html',
  styleUrls: ['./user.component.css']
})
export class UserComponent implements OnInit {
  board: string;
  errorMessage: string;

  constructor(private userService: UserService) { }

  ngOnInit() {
    this.userService.getUserBoard().subscribe(
      data => {
        this.board = data;
      },
      error => {
        this.errorMessage = `${error.status}: ${JSON.parse(error.error).message}`;
      }
    );
  }
}

pm.component.ts

import { Component, OnInit } from '@angular/core';
import { UserService } from '../services/user.service';

@Component({
  selector: 'app-pm',
  templateUrl: './pm.component.html',
  styleUrls: ['./pm.component.css']
})
export class PmComponent implements OnInit {
  board: string;
  errorMessage: string;

  constructor(private userService: UserService) { }

  ngOnInit() {
    this.userService.getPMBoard().subscribe(
      data => {
        this.board = data;
      },
      error => {
        this.errorMessage = `${error.status}: ${JSON.parse(error.error).message}`;
      }
    );
  }
}

admin.component.ts

import { Component, OnInit } from '@angular/core';
import { UserService } from '../services/user.service';

@Component({
  selector: 'app-admin',
  templateUrl: './admin.component.html',
  styleUrls: ['./admin.component.css']
})
export class AdminComponent implements OnInit {
  board: string;
  errorMessage: string;

  constructor(private userService: UserService) { }

  ngOnInit() {
    this.userService.getAdminBoard().subscribe(
      data => {
        this.board = data;
      },
      error => {
        this.errorMessage = `${error.status}: ${JSON.parse(error.error).message}`;
      }
    );
  }
}

App Routing

app-routing.module.ts

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

import { RegisterComponent } from './register/register.component';
import { LoginComponent } from './login/login.component';
import { HomeComponent } from './home/home.component';
import { UserComponent } from './user/user.component';
import { PmComponent } from './pm/pm.component';
import { AdminComponent } from './admin/admin.component';

const routes: Routes = [
    {
        path: 'home',
        component: HomeComponent
    },
    {
        path: 'user',
        component: UserComponent
    },
    {
        path: 'pm',
        component: PmComponent
    },
    {
        path: 'admin',
        component: AdminComponent
    },
    {
        path: 'auth/login',
        component: LoginComponent
    },
    {
        path: 'signup',
        component: RegisterComponent
    },
    {
        path: '',
        redirectTo: 'home',
        pathMatch: 'full'
    }
];

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

app.component.ts

import { Component, OnInit } from '@angular/core';
import { TokenStorageService } from './auth/token-storage.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  private roles: string[];
  private authority: string;

  constructor(private tokenStorage: TokenStorageService) { }

  ngOnInit() {
    if (this.tokenStorage.getToken()) {
      this.roles = this.tokenStorage.getAuthorities();
      this.roles.every(role => {
        if (role === 'ROLE_ADMIN') {
          this.authority = 'admin';
          return false;
        } else if (role === 'ROLE_PM') {
          this.authority = 'pm';
          return false;
        }
        this.authority = 'user';
        return true;
      });
    }
  }
}

app.component.html

<nav class="navbar navbar-inverse">
  <div class="container-fluid">
    <div class="navbar-header">
      <a class="navbar-brand" href="#">ozenero</a>
    </div>
    <ul class="nav navbar-nav" routerLinkActive="active">
      <li class="nav-item"><a class="nav-link" routerLink="home">Home</a></li>
      <li *ngIf="authority === 'user'" class="nav-item">
        <a class="nav-link" routerLink="user">User Board</a>
      </li>
      <li *ngIf="authority === 'admin'" class="nav-item">
        <a class="nav-link" routerLink="admin">Admin Board</a>
      </li>
      <li *ngIf="authority === 'pm'" class="nav-item">
        <a class="nav-link" routerLink="pm">PM Board</a>
      </li>
      <li *ngIf="!authority" class="nav-item">
        <a class="nav-link" routerLink="auth/login">Login</a>
      </li>
    </ul>
  </div>
</nav>
<div class="container">
  <router-outlet></router-outlet>
</div>

SourceCode

AngularJwtAuth

129 thoughts on “Angular 14 Spring Boot JWT Authentication example | Angular 14 + Spring Security + MySQL Full Stack – Part 3: Build Frontend”

  1. I’m really enjoying the theme/design of your site.
    Do you ever run into any internet browser compatibility issues?

    A small number of my blog readers have complained about my site not working correctly in Explorer but looks great in Opera.

    Do you have any advice to help fix this issue?

  2. Hello there, just changed into alert to your weblog thru Google, and located that it’s really informative. I am going to be careful for brussels. I will appreciate when you proceed this in future. Lots of folks shall be benefited out of your writing. Cheers!

  3. I think this is one of the most important info for me. And i
    am glad reading your article. But want to remark on few general
    things, The website style is perfect, the articles is really excellent : D.
    Good job, cheers

  4. Hi there, I found your web site by means of Google at the same time as searching for a related subject, your website
    got here up, it appears to be like good. I’ve bookmarked it in my google bookmarks.

    Hi there, simply changed into aware of your blog
    thru Google, and located that it is really informative.
    I’m gonna be careful for brussels. I will appreciate if you happen to continue this in future.
    Many other folks shall be benefited out of your writing.
    Cheers!

  5. I love what you guys are up too. This kind of
    clever work and reporting! Keep up the awesome works
    guys I’ve added you guys to my own blogroll.

  6. Hello, i think that i saw you visited my site thus
    i came to “return the favor”.I am attempting to find
    things to enhance my web site!I suppose its
    ok to use a few of your ideas!!

  7. Please let me know if you’re looking for a article author for your site.

    You have some really great posts and I believe I would be a
    good asset. If you ever want to take some of the load off, I’d love to write
    some material for your blog in exchange for a link back to
    mine. Please send me an e-mail if interested. Kudos!

  8. Today, I went to the beachfront with my kids. I found
    a sea shell and gave it to my 4 year old daughter and said “You can hear the ocean if you put this to your ear.” She put the shell
    to her ear and screamed. There was a hermit crab inside and it pinched
    her ear. She never wants to go back! LoL I know this is totally off topic but I had to
    tell someone!

  9. Hello! This post could not be written any better! Reading this post reminds
    me of my old room mate! He always kept chatting
    about this. I will forward this post to him. Pretty sure he will
    have a good read. Thank you for sharing!

  10. Howdy! I could have sworn I’ve been to this website before
    but after browsing through some of the posts I realized it’s new to me.
    Anyhow, I’m definitely delighted I found it
    and I’ll be bookmarking it and checking back frequently!

  11. I think everything said was very reasonable.
    But, consider this, suppose you wrote a catchier post title?
    I am not saying your content is not good, but what if you added a title
    that grabbed folk’s attention? I mean ozenero | Mobile & Web Programming
    Tutorials is kinda boring. You might look at Yahoo’s home page and watch how they create article headlines
    to grab people to click. You might add a video or a pic
    or two to get readers excited about everything’ve got to say.

    Just my opinion, it might make your posts a little bit more
    interesting.

  12. Fantastic goods from you, man. I’ve consider your stuff prior to
    and you are just extremely great. I really like what you’ve bought here,
    certainly like what you’re saying and the way during which you
    are saying it. You are making it entertaining and you still care for to keep it wise.
    I can not wait to read much more from you. That is actually a tremendous site.

  13. When I originally commented I clicked the “Notify me when new comments are added”
    checkbox and now each time a comment is added I get several emails with the same comment.
    Is there any way you can remove people from that
    service? Many thanks!

  14. We are a bunch of volunteers and opening a new scheme in our community.
    Your site provided us with valuable info
    to work on. You have performed a formidable task and our entire community will likely be thankful to you.

  15. I am really impressed together with your writing talents and
    also with the format to your blog. Is that this a paid theme or did you customize it your self?
    Anyway keep up the nice high quality writing, it’s uncommon to peer a nice blog like this one these days..

  16. I’m extremely inspired with your writing talents as
    neatly as with the format for your blog. Is that this a paid
    topic or did you modify it your self? Anyway stay up the excellent high
    quality writing, it’s rare to see a great blog like this one these days..

  17. Wow, superb blog format! How lengthy have you been blogging for?

    you make running a blog look easy. The entire look of your website is fantastic,
    let alone the content!

  18. I just couldn’t go away your site before suggesting that I extremely
    loved the usual info an individual supply in your
    guests? Is gonna be again regularly to check up on new
    posts

  19. You have made some decent points there. I looked on the net for more info about the issue and found most individuals will go along with your views on this site.

  20. Attractive portion of content. I simply stumbled upon your site and in accession capital to assert that
    I get actually loved account your blog posts. Any way I’ll be subscribing
    on your augment or even I fulfillment you access consistently
    fast.

  21. Please let me know if you’re looking for a
    author for your weblog. You have some really great posts
    and I feel I would be a good asset. If you ever want to take some
    of the load off, I’d absolutely love to write some material
    for your blog in exchange for a link back to mine.
    Please send me an email if interested. Regards!

  22. Hi, i believe that i noticed you visited my weblog thus i got
    here to go back the favor?.I am attempting to to find things
    to enhance my site!I guess its ok to use some of your ideas!!

  23. Every weekend i used to pay a visit this web site, as i wish for
    enjoyment, since this this site conations actually nice funny material too.

  24. Do you mind if I quote a few of your articles as long as I provide credit and sources back to your blog?

    My blog is in the exact same area of interest as yours
    and my visitors would really benefit from a lot of the information you
    present here. Please let me know if this okay with you.
    Many thanks!

  25. Many people want to get fit, but assume that
    it’s hard to accomplish. Really the only way you can absolutely achieve it yourself is usually to learn the correct stuff and be absolutely knowledgeable about exercise.
    You may use the recommendation in this article to discover your end target.

  26. Definitely believe that which you said. Your favorite reason appeared to be on the web the
    simplest thing to be aware of. I say to you, I definitely get annoyed while people consider
    worries that they plainly do not know about. You managed
    to hit the nail upon the top and also defined out the whole thing without having side effect ,
    people could take a signal. Will likely be back to get more.
    Thanks

  27. I’ll immediately grasp your rss as I can’t in finding your email
    subscription hyperlink or newsletter service.
    Do you have any? Kindly permit me recognize in order that
    I may just subscribe. Thanks.

  28. Just desire to say your article is as amazing. The clarity in your post is just
    great and i could assume you’re an expert on this subject.
    Fine with your permission let me to grab your RSS feed to keep
    up to date with forthcoming post. Thanks a million and please carry on the gratifying work.

  29. I simply couldn’t depart your website prior to suggesting that I really enjoyed the
    standard info a person provide on your visitors? Is gonna
    be again regularly to investigate cross-check new
    posts

  30. I’d like to thank you for the efforts you have put in writing this website.
    I really hope to view the same high-grade blog posts from you later on as well.
    In truth, your creative writing abilities has inspired me to get my own blog now 😉

  31. Today, I went to the beach with my children. I found a sea
    shell and gave it to my 4 year old daughter and said “You can hear the ocean if you put this to your ear.” She placed the
    shell to her ear and screamed. There was a hermit crab inside and it pinched her ear.
    She never wants to go back! LoL I know this is totally
    off topic but I had to tell someone!

  32. Very nice post. I just stumbled upon your blog and wished
    to say that I have really enjoyed surfing around
    your blog posts. After all I’ll be subscribing to
    your rss feed and I hope you write again very soon!

  33. Great blog you have here but I was curious if you knew of any discussion boards that cover the same topics
    discussed here? I’d really love to be a part of online community where I can get feedback from other knowledgeable individuals that share the same interest.

    If you have any suggestions, please let me know. Many
    thanks!

  34. You have made some decent points there. I checked on the net to learn more about the issue and found most individuals
    will go along with your views on this website.

  35. What i don’t understood is in truth how you are now not really much
    more smartly-appreciated than you might be right now.

    You are very intelligent. You know thus considerably in relation to this topic,
    made me personally consider it from numerous numerous
    angles. Its like men and women aren’t fascinated until it’s something to accomplish with Lady gaga!
    Your individual stuffs outstanding. At all times deal with it up!

  36. Hello there! Would you mind if I share your blog with my facebook group?
    There’s a lot of folks that I think would really enjoy your content.
    Please let me know. Many thanks

  37. Beim Hotelaufenthalt hingegen befindet sich der Gast nach Verlassen seines
    Zimmers sofort in der Öffentlichkeit. Der gesamte Aufenthalt
    lässt sich lockerer und ungezwungener gestalten. Über
    die obigen Angebote und die Suchmaske können Sie viele günstige Skireisen miteinander vergleichen. Pluralistisch Reisezielen bietet den Vorteil, dass für jede person Mann fürs Leben Skiurlaub 2013
    vorhanden ist. Urlaub im Schnee ist meist ein sportlich-aktiver
    Urlaub und der Tagesablauf in den Skiferien wird weitgehend von Abfahrtski, Skilanglauf und
    Rodeln bestimmt. Die einen freuen sich auf einen sonnigen Sommerurlaub,
    andere können den Winterurlaub kaum erwarten und buchen schon Monate
    vorher eine attraktive Skireise. Das gilt besonders für einen Familienurlaub mit mehreren Personen. Diese ist in vielen Fällen hierbei Aufenthalt in einem Ferienhaus verbunden. Bei der Auswahl einer Unterkunft für den Skiurlaub haben Ferienwohnungen oder Ferienhäuser einige Vorteile gegenüber dem Hotel.
    Neben dem spürbar günstigeren Preis bieten Wohnungen und Ferienhäuser deutlich mehr Flexibilität, denn für die Mahlzeiten gibt es beispielsweise im Gegensatz
    zur Halb- oder Vollpension im Hotel keinerlei Vorgaben.

  38. Denn nur dieser kann das tatsächliche Alter bestätigen. Wie schnell kann man sich bei einem Sturz schwer
    verletzen, nur weil ein Stein vom Schnee bedeckt war.

    Übrigens: Wer sich nicht ausweisen kann, hat leider mit Strafzetteln,
    also Bußgeldern zu rechnen. Vielerorts ist mittlerweile eine
    Helmpflicht in Kraft getreten. Id est, dass im europäischen Ausland mindestens der
    Personalausweis, besser aber der Reisepass parat ist.
    Zudem wird in vielen Skiorten der für Schüler, Studenten und Rentner vergünstigte Skipass nur noch nach Vorlage eines entsprechenden,
    offiziellen Ausweises verkauft. Zudem ist es wichtig, im
    ausland jederzeit in der Lage nicht sinnvoll, sich ausweisen zu können. Damit die Verletzungen am Kopf nicht so schwerwiegend werden, wenn es tatsächlich einmal
    zum Sturz kommt, sollte ein Helm getragen werden. Wird diese nicht eingehalten, kann es zu
    drastischen Ordnungsstrafen, also Bußgeldern, kommen. Das muss
    soll mir nicht sein, oder? Helmpflicht: Es ist richtig, dass ein Helm jeden Skifahrer vor schweren Kopfverletzungen schützen kann.

  39. Da warten u.a. Saunen und einem Indoor- und Outdoorpool.
    Außerdem wurde es bereits mehrfach zum besten Skigebiet der Welt gekürt!
    Direkt bei den Liften des Skigebiets Obergurgl – Hochgurgl
    steht das Top bewertete 4°S The Crystal mit seinem fantastischen 2.000 m² Spa-Bereich auf 2 Etagen. Die Gründe liegen klar auf der Hand:
    Neben den modernsten Aufstiegsanlagen und den vielen traditionellen Hütten zum Einkehren findet ihr hier… Servus Piraten! Hinter jemandem her einen luxuriösen Trip ganz ans Ende des Ötztals?
    Dahinter stehen zwei bekannte Persönlichkeiten,
    und zwar Hermann Maier & Rainer Schönfelder,
    welche mit dieser neuen Hotelmarke (ursprünglicher Name adeo) Skiurlaub für jedermann
    erschwinglich machen, was ihnen mit ihren Hotelprojekten wahrlich gut… Mit fast 300 Pistenkilometern und knapp 100 Liftanlagen gehört die SkiWelt
    Wilder Kaiser-Brixental zu den größten und modernsten Skigebieten Österreichs und der Welt.
    Auch zum diesjährigen Saisonfinale Ende März bekommt ihr noch Termine.
    Falls ihr diesjährig nicht zum Rennen dort wart,
    macht das nichts, denn Snowtrex hat bereits die Buchungstermine für die
    kommenden Tage Jahr drin, bei denen ihr arg sparen könnt.
    Ahoi Piraten! Matthias Mayer heisst der diesjährige Sieger des Hahnenkammrennens
    auf der Kitzbühler “Streif”. Wem die 110 km Piste auf einer Höhe zwischen…

  40. Nur aus dringenden betrieblichen oder in der Person des Arbeitnehmers liegenden Gründen ist eine Übertragung des Urlaubs aufs nächste Kalenderjahr statthaft; unter
    diesen Umständen muss der Urlaub in den ersten drei
    Monaten des folgenden Kalenderjahrs gewährt und
    genommen werden. Kuren und Schonzeiten dürfen nicht auf den Urlaub angerechnet werden, soweit ein Anspruch auf Entgeltfortzahlung im Krankheitsfall (Entgeltfortzahlung) besteht.
    Während des Bestehens des Arbeitsverhältnisses gilt ein Abgeltungsverbot.
    6. Vergütung: Urlaubsentgelt, Urlaubsgeld. Erkrankt
    ein Arbeitnehmer während des Urlaubs, so werden die durch ärztliches Zeugnis nachgewiesenen Tage der Arbeitsunfähigkeit auf den Jahresurlaub nicht angerechnet.
    Kann der Urlaub wegen Beendigung des Arbeitsverhältnisses ganz oder teilweise nicht
    länger gewährt werden, so ist er abzugelten. Mit Ablauf des
    Übertragungszeitraums wird der Arbeitgeber von welcher Verpflichtung zur Urlaubsgewährung frei, soweit er nicht die Unmöglichkeit der Urlaubsgewährung zu
    vertreten hat. Hat der Arbeitnehmer im laufenden Urlaubsjahr
    nur einen Teilanspruch wegen nicht erfüllter Wartezeit, so ist dieser Urlaub auf verlangen des Arbeitnehmers auf Gesamteindruck nächste Urlaubsjahr zu übertragen und dabei
    Urlaub des folgenden Jahres zu gewähren.

  41. Seit Mitte Juli liegt die auch R-Wert genannte Ansteckungsrate wieder häufig über 1 und das heißt,
    dass sich der Pool an Infizierten wieder in langsamem Tempo
    vergrößert. Nicht unbedingt schnell wie im Frühjahr, aber je länger der leichte Anstieg andauert,
    umso größer ist die Wahrscheinlichkeit, dass doch wieder Fallzahlen deutlich
    oberhalb der 2000er-Marke zu dem Ergebnis gelangen. Entsprechend verharren die täglich registrierten Todesfallzahlen seit Juni auf einem
    sehr niedrigen Niveau im meist einstelligen Bereich.

    Sollte das Virus aber aus der Gruppe der Unter-35-Jährigen heraus wieder seinen Weg hin zu den Älteren finden, können auch die aktuell niedrigen Todesfallzahlen erneut ansteigen.
    Wo genau infizieren sich die Menschen mit dem Coronavirus? Zum Vergleich:
    Im Frühjahr wurden täglich bis zu 250 Todesfälle durch oder mit Corona-Infektionen gemeldet.
    Nach Schätzung des RKI steckte im April ein Covid-19-Infizierter im Mittel
    vier weitere Personen an. Dieser Wert sank bis Juni auf Werte deutlich unter 1.
    Das bedeutet, dass damals die Zahl der Infizierten von Tag zu Tag abnahm.
    Dann wird man einen weiteren Vergleich ziehen können – nämlich den, wie das Gesundheitssystem im Vergleich zum Frühjahr
    mit den hohen Fallzahlen zurechtkommt. Dass Sars-CoV-2 der Sprung zwischen den Altersgruppen diesmal vielleicht nicht so schnell gelingt, dafür spricht
    die Ansteckungsrate.

  42. I just like the valuable info you provide for your articles.
    I will bookmark your weblog and take a look at once more here frequently.
    I’m reasonably sure I’ll be told plenty of new stuff right right here!
    Good luck for the next!

  43. Hi tһere, i read your blg occasionalpy and i oown a similar one and i was just
    curious if you geet а lօt of spam remarks? If so how do you protect against it, any plugin or аnything you
    can advise? I get ѕo much lately it’s driving me crazy so any assistance is very much appreciated.

  44. Lieferung und Abholung ist aber weiterhin möglich.

    Ab 28.04. dürfen die Außenbereiche von Gastronomiebetrieben wieder öffnen. Einreisebestimmungen: Seit dem 01.09.2020 dürfen deutsche Touristen infolge steigenden Fallzahlen nur mehr
    mit einem triftigen Reisegrund nach Ungarn einreisen! Bei der Öffnung
    der Hotels gibt es regional Unterschiede. Risikogebiete:
    Seit dem 1. November warnt das Auswärtig Amt vor nicht notwendigen, touristischen Reisen nach Ungarn. Es kann zu geänderten Öffnungszeiten kommen. Im öffentlichen Raum
    ist zudem der Konsum von Alkohol ab 20 Uhr untersagt. Discos und Nachtclubs bleiben ebenfalls geschlossen. Das Einkaufen ist nur mit Terminabsprache möglich.
    Geschäfte: Unter Auflagen geöffnet. Ansonsten muss ein Abstand von 1,5 Metern Abstand
    wirken wie. Lockdown: nein, es ist bis 28.04. eine nächtliche Ausgangssperre zwischen 22
    Uhr und 4.30 Uhr. Das Land wurde vom RKI zur kompletten Risikozone erklärt.
    Negativer PCR Test / Quarantäne: Ja, 10 Tage. Maskenpflicht und Hygienemaßnahmen: Maskenpflicht besteht in öffentlichen Verkehrsmitteln, Fähren, Taxis und am Flughafen und in geschlossenen und öffentlichen Räumen. Hotels und Unterkünfte:
    Sind wieder geöffnet. Gastronomie: Cafés, Restaurants und Kneipen haben geschlossen.

  45. Howdy would you mind sharing which blog platform you’re using?

    I’m going to start my own blog in the near future but I’m having a hard time deciding between BlogEngine/Wordpress/B2evolution and Drupal.
    The reason I ask is because your design seems different then most blogs and I’m
    looking for something unique. P.S My apologies for being off-topic but I had to
    ask!

  46. I think that everything said was actually very reasonable.
    But, think on this, what if you were to write a awesome title?
    I am not saying your content isn’t good., but suppose you added something that makes people want more?
    I mean ozenero | Mobile & Web Programming Tutorials is a little plain. You ought to glance at Yahoo’s front page and note how they create news titles to grab
    viewers interested. You might add a related video or a pic or two
    to grab people interested about everything’ve got to say.
    Just my opinion, it might make your posts a little bit more
    interesting.

  47. May I simply just say what a relief to find a person that really knows what they are discussing on the net.

    You definitely understand how to bring an issue to light and make it important.
    More people need to read this and understand this side of your story.
    I was surprised that you are not more popular given that you definitely have the gift.

  48. With havin so much written content do you ever run into any issues of plagorism or copyright infringement?

    My blog has a lot of completely unique content I’ve either written myself or outsourced but it appears a lot of it is
    popping it up all over the internet without my agreement.
    Do you know any ways to help stop content from being stolen? I’d definitely
    appreciate it.

  49. I think this is one of the most significant information for
    me. And i’m glad reading your article. But should remark on few general
    things, The website style is perfect, the articles is really nice : D.
    Good job, cheers

  50. You have made some decent points there. I checked on the internet for additional information about the issue and found most people will go along with your views on this web site.

  51. Hmm is anyone else encountering problems with the pictures
    on this blog loading? I’m trying to figure out if its a problem on my end or if it’s the blog.
    Any feed-back would be greatly appreciated.

  52. Hi, Neat post. There is a problem along with your website in internet explorer, would check
    this? IE still is the marketplace chief and a big component to
    people will leave out your wonderful writing due to this problem.

  53. I just could not leave your website prior to suggesting that I really loved the standard information a person provide to your guests?
    Is gonna be again ceaselessly in order to check out new posts

  54. Based on my observation, after a in foreclosure process home is available at a sale, it is common with the borrower to be able to still have some sort ofthat remaining balance on the personal loan. There are many loan companies who try and have all service fees and liens paid back by the up coming buyer. On the other hand, depending on particular programs, restrictions, and state laws and regulations there may be several loans that are not easily handled through the switch of financial products. Therefore, the responsibility still falls on the borrower that has obtained his or her property in foreclosure. Many thanks sharing your thinking on this site.

  55. Definitely believe that which you said. Your favorite reason seemed to be on the internet the easiest thing to be aware of.
    I say to you, I definitely get annoyed while people think about worries that they just don’t know about.
    You managed to hit the nail upon the top as well as defined out the whole thing without having side-effects
    , people could take a signal. Will probably be back to get more.
    Thanks

  56. I like the valuable info you provide for your articles. I’ll bookmark your weblog and
    test once more here frequently. I am reasonably sure I’ll be told a lot of new stuff proper right here!
    Good luck for the next!

  57. When I initially commented I clicked the “Notify me when new comments are added”
    checkbox and now each time a comment is added I get several e-mails with the same comment.
    Is there any way you can remove me from that service?
    Appreciate it!

  58. Have you ever considered publishing an ebook or guest
    authoring on other sites? I have a blog based on the same information you
    discuss and would really like to have you share some stories/information. I know my audience would enjoy your work.
    If you’re even remotely interested, feel free to send me an e mail.

  59. We’re a group of volunteers and starting a brand new scheme in our
    community. Your site offered us with valuable information to
    work on. You’ve performed a formidable job and our whole group will probably
    be grateful to you.

  60. Hi! I could have sworn I’ve been to this site before but after looking
    at a few of the posts I realized it’s new to me. Anyhow, I’m definitely pleased I came across it and I’ll be
    bookmarking it and checking back often!

  61. Admiring the persistence you put into your blog and
    detailed information you provide. It’s awesome to come across a blog
    every once in a while that isn’t the same unwanted rehashed
    material. Wonderful read! I’ve bookmarked your site and I’m adding your RSS
    feeds to my Google account.

  62. Hello there! I know this is kind of off topic but I was wondering if you knew where I could locate a captcha plugin for my comment form?
    I’m using the same blog platform as yours and I’m having problems finding one?
    Thanks a lot!

  63. Generally I don’t read post on blogs, but I would like to
    say that this write-up very pressured me to take a look at and do it!
    Your writing taste has been amazed me. Thank
    you, quite nice article.

  64. After exploring a few of the blog articles on your web page, I really like your technique
    of writing a blog. I book marked it to my bookmark website list and will be checking back
    soon. Please visit my web site as well and let me know what
    you think.

  65. Hello! This is my first visit to your blog! We are a group of volunteers and starting a new
    project in a community in the same niche. Your blog provided
    us valuable information to work on. You have done a extraordinary
    job!

Leave a Reply

Your email address will not be published.