Angular Spring Boot JWT Authentication example

Tags: angular, springboot, jwt, authentication
Published: 2021-03-16 12:16:55

https://grokonez.com/frontend/angular/angular-6/angular-spring-boot-jwt-authentication-example-angular-6-spring-security-mysql-full-stack-part-3-build-frontend

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

<strong>Part 1</strong>: <a href="https://grokonez.com/spring-framework/spring-boot/angular-spring-boot-jwt-authentication-example-angular-6-spring-security-mysql-full-stack-part-1-overview-and-architecture">Overview and Architecture</a>.
<strong>Part 2</strong>: <a href="https://grokonez.com/spring-framework/spring-boot/angular-spring-boot-jwt-authentication-example-angular-6-spring-security-mysql-full-stack-part-2-build-backend">Build SpringBoot Backend</a>

<!--more-->
Related Posts:
- <a href="https://grokonez.com/frontend/angular-6-http-interceptor-with-node-js-restapis">Angular 6 Http Interceptor – with Node.js RestAPIs</a>
- <a href="https://grokonez.com/spring-framework/spring-boot/angular-6-httpclient-get-post-put-delete-requests-springboot-restapis-bootstrap-4">Angular 6 HttpClient – Get/Post/Put/Delete requests + SpringBoot RestAPIs + Bootstrap 4</a>

<h2>How to build JWT Authentication frontend with Angular</h2>
<h3>Demo</h3>
<iframe width="853" height="480" src="https://www.youtube.com/embed/VqfYbxbYp5I?rel=0" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>
<h3>Send Requests to Server</h3>
<h4>Add Token to Header with HttpInterceptor</h4>
We use Angular <code>HttpInterceptor</code> with <code>intercept()</code> method to inspect and transform HTTP requests (before they are sent to server):
<pre><code class="language-javascript">
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<any>, 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 }
];
</code></pre>
- The <code>HTTPRequest</code> object will be inspected and forwarded to <code>handle()</code> method of the <code>HttpHandler</code> object.
- <code>handle()</code> method transforms <code>HTTPRequest</code> object into an <code>Observable</code> of <code>HttpEvents</code> which includes the server’s response.

What is <code>next: HttpHandler</code> object?
-> This object represents the next <code>interceptor</code> in the chain of <code>interceptor</code>s. The final 'next' in the chain is the Angular <code>HttpClient</code> handler.
<div class="jsa-quote">
For more details, please visit:
<a href="https://grokonez.com/frontend/angular-6-http-interceptor-with-node-js-restapis">Angular 6 Http Interceptor – with Node.js RestAPIs</a>
</div>
<h4>HTTP Request using HttpClient</h4>
We send HTTP Requests (signin/signup) using Angular <code>HttpClient</code>:
<pre><code class="language-javascript">
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<JwtResponse> {
    return this.http.post<JwtResponse>(this.loginUrl, credentials, httpOptions);
  }

  // SignUpInfo(name,username,email,role,password)
  signUp(info: SignUpInfo): Observable<string> {
    return this.http.post<string>(this.signupUrl, info, httpOptions);
  }
}
</code></pre>
<code>AuthLoginInfo</code> fields & <code>SignUpInfo</code> fields are validated using Angular template-driven Form.

<div class="jsa-quote">
For more details, please visit:
<a href="https://grokonez.com/frontend/angular-6-form-validation-example-template-driven-forms">Angular 6 Form Validation example – Template-driven Forms</a>
</div>
Once Token is saved, we can access protected resources simply:
<pre><code class="language-javascript">
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<string> {
    return this.http.get(this.userUrl, { responseType: 'text' });
  }

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

  getAdminBoard(): Observable<string> {
    return this.http.get(this.adminUrl, { responseType: 'text' });
  }
}
</code></pre>
<h3>Handle Responses</h3>
Using <code>AuthService</code> to work with <code>Observable</code> object:
<h4>Signup Response</h4>
<pre><code class="language-javascript">
export class RegisterComponent implements OnInit {
  ...
  constructor(private authService: AuthService) { }

  onSubmit() {
    ...
    this.authService.signUp(this.signupInfo).subscribe(
      data => {
        ...
      },
      error => {
        ...
      }
    );
  }
}
</code></pre>
<h4>Login Response</h4>
In addition to using <code>AuthService</code> to work with <code>Observable</code> object, we also call <code>TokenStorageService</code> methods to save Token, Username, Authorities:
<pre><code class="language-javascript">
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 => {
        ...
      }
    );
  }
}
</code></pre>
<h3>Manage Token & User Logout</h3>
We use <code>TokenStorageService</code> to manage Token inside Browser’s <code>sessionStorage</code>:
<pre><code class="language-javascript">
export class TokenStorageService {
  private roles: Array<string> = [];

  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;
  }
}
</code></pre>
For Logout action, we only need to clear Browser's <code>sessionStorage</code>:
<pre><code class="language-javascript">
export class TokenStorageService {
  ...
  signOut() {
    window.sessionStorage.clear();
  }
}
</code></pre>
<h2>Angular Client for JWT Authentication Overview</h2>
<h3>Goal</h3>
We will build <strong><em>Angular</em></strong> Client which allows users to register, login account. And depending on the role of current User (<em>user</em>, <em>pm</em> or <em>admin</em>), this system accepts what he can access:

<img src="https://grokonez.com/wp-content/uploads/2018/10/spring-boot-angular-spring-security-jwt-authentication-demo-goal.png" alt="spring-boot-angular-spring-security-jwt-authentication-demo-goal" width="750" height="1050" class="alignnone size-full wp-image-15307" />

The diagram below show how our system handles <strong>User Registration</strong> and <strong>User Login</strong> processes:

<img src="https://grokonez.com/wp-content/uploads/2018/10/angular-spring-security-jwt-authentication-work-process-diagram.png" alt="angular-spring-security-jwt-authentication-work-process-diagram" width="730" height="475" class="alignnone size-full wp-image-15310" />
<h5>Elements Overview</h5>
<img src="https://grokonez.com/wp-content/uploads/2018/10/spring-boot-angular-spring-security-jwt-authentication-architecture-diagram-front-end-client.png" alt="spring-boot-angular-spring-security-jwt-authentication-architecture-diagram-front-end-client" width="750" height="380" class="alignnone size-full wp-image-15320" />

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

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

- <code>token-storage.service</code> manages Token inside Browser's <code>sessionStorage</code>.
<div class="jsa-quote">
For more details about:
<ul>
<li>Interceptor: <a href="https://grokonez.com/frontend/angular-6-http-interceptor-with-node-js-restapis">Angular 6 Http Interceptor – with Node.js RestAPIs</a></li>
<li>Routing: <a href="https://grokonez.com/frontend/angular-6-routing-navigation-with-angular-router-service">Angular 6 Routing/Navigation – with Angular Router Service</a></li>
<li>Form Validation: <a href="https://grokonez.com/frontend/angular-6-form-validation-example-template-driven-forms">Angular 6 Form Validation example – Template-driven Forms</a></li>
<li>Angular 6 HttpClient example: <a href="https://grokonez.com/spring-framework/spring-boot/angular-6-httpclient-get-post-put-delete-requests-springboot-restapis-bootstrap-4">Angular 6 HttpClient – Get/Post/Put/Delete requests + SpringBoot RestAPIs + Bootstrap 4</a></li>
</ul>
</div>
<h5>Technologies</h5>
- Angular 6
- RxJS 6
<h3>Project Structure</h3>
<img src="https://grokonez.com/wp-content/uploads/2018/10/spring-boot-angular-spring-security-jwt-authentication-front-end-angular-project-structure.png" alt="spring-boot-angular-spring-security-jwt-authentication-front-end-angular-project-structure" width="251" height="573" class="alignnone size-full wp-image-15417" />
<h2>Practice</h2>
<h3>Create Services & Components</h3>
Run commands below:
- <code>ng g s auth/auth</code>
- <code>ng g s auth/token-storage</code>
- <code>ng g s services/user</code>

- <code>ng g c login</code>
- <code>ng g c register</code>
- <code>ng g c home</code>
- <code>ng g c user</code>
- <code>ng g c pm</code>
- <code>ng g c admin</code>
<h3>AppModule</h3>
<em>app.module.ts</em>
<pre><code class="language-javascript">
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 { }
</code></pre>
<h3>Services</h3>
<h4>Auth Models</h4>
<em>auth/login-info.ts</em>
<pre><code class="language-javascript">
export class AuthLoginInfo {
    username: string;
    password: string;

    constructor(username: string, password: string) {
        this.username = username;
        this.password = password;
    }
}
</code></pre>
<em>auth/sigup-info.ts</em>
<pre><code class="language-javascript">
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'];
    }
}
</code></pre>
<em>auth/jwt-response.ts</em>
<pre><code class="language-javascript">
export class JwtResponse {
    accessToken: string;
    type: string;
    username: string;
    authorities: string[];
}
</code></pre>
<h4>Auth Service</h4>
<em>auth/auth.service.ts</em>
<pre><code class="language-javascript">
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<JwtResponse> {
    return this.http.post<JwtResponse>(this.loginUrl, credentials, httpOptions);
  }

  signUp(info: SignUpInfo): Observable<string> {
    return this.http.post<string>(this.signupUrl, info, httpOptions);
  }
}
</code></pre>
<h4>Token Storage Service</h4>
<em>auth/token-storage.service.ts</em>
<pre><code class="language-javascript">
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<string> = [];
  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;
  }
}
</code></pre>
<h4>User Service</h4>
<em>services/user.service.ts</em>
<pre><code class="language-javascript">
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<string> {
    return this.http.get(this.userUrl, { responseType: 'text' });
  }

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

  getAdminBoard(): Observable<string> {
    return this.http.get(this.adminUrl, { responseType: 'text' });
  }
}</code></pre>
<h3>HTTP Interceptor</h3>
<em>auth/auth-interceptor.ts</em>
<pre><code class="language-javascript">
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<any>, 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 }
];
</code></pre>
<h3>Components</h3>
<h4>Home Component</h4>
<em>home.component.ts</em>
<pre><code class="language-javascript">
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();
  }
}
</code></pre>
<em>home.component.html</em>
<pre><code class="language-html">&lt;div *ngIf=&quot;info.token; else loggedOut&quot;&gt;
  &lt;h5 class=&quot;text-primary&quot;&gt;Your Information&lt;/h5&gt;
  &lt;p&gt;
    &lt;strong&gt;Username:&lt;/strong&gt;  { { info.username}}&lt;br/&gt;
    &lt;strong&gt;Roles:&lt;/strong&gt;  { { info.authorities}}&lt;br /&gt;
    &lt;strong&gt;Token:&lt;/strong&gt;  { { info.token}}.
  &lt;/p&gt;
  &lt;button class=&quot;btn btn-secondary&quot; (click)=&quot;logout()&quot;&gt;Logout&lt;/button&gt;
&lt;/div&gt;

&lt;ng-template #loggedOut&gt;
  Please login.
&lt;/ng-template&gt;</code></pre>
<h4>Login Component</h4>
<em>login.component.ts</em>
<pre><code class="language-javascript">
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();
  }
}
</code></pre>
<em>login.component.html</em>
<pre><code class="language-html">&lt;div *ngIf=&quot;isLoggedIn; else loggedOut&quot;&gt;
  Logged in as  { { roles}}.
&lt;/div&gt;

&lt;ng-template #loggedOut&gt;
  &lt;div class=&quot;row col-sm-6&quot; style=&quot;max-width:350px;&quot;&gt;
    &lt;form name=&quot;form&quot; (ngSubmit)=&quot;f.form.valid &amp;&amp; onSubmit()&quot; #f=&quot;ngForm&quot; novalidate&gt;
      &lt;div class=&quot;form-group&quot;&gt;
        &lt;label for=&quot;username&quot;&gt;Username&lt;/label&gt;
        &lt;input type=&quot;text&quot; class=&quot;form-control&quot; name=&quot;username&quot; [(ngModel)]=&quot;form.username&quot; #username=&quot;ngModel&quot;
          required /&gt;
        &lt;div *ngIf=&quot;f.submitted &amp;&amp; username.invalid&quot;&gt;
          &lt;div *ngIf=&quot;username.errors.required&quot;&gt;Username is required&lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
      &lt;div class=&quot;form-group&quot;&gt;
        &lt;label for=&quot;password&quot;&gt;Password&lt;/label&gt;
        &lt;input type=&quot;password&quot; class=&quot;form-control&quot; name=&quot;password&quot; [(ngModel)]=&quot;form.password&quot; #password=&quot;ngModel&quot;
          required minlength=&quot;6&quot; /&gt;
        &lt;div *ngIf=&quot;f.submitted &amp;&amp; password.invalid&quot;&gt;
          &lt;div *ngIf=&quot;password.errors.required&quot;&gt;Password is required&lt;/div&gt;
          &lt;div *ngIf=&quot;password.errors.minlength&quot;&gt;Password must be at least 6 characters&lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
      &lt;div class=&quot;form-group&quot;&gt;
        &lt;button class=&quot;btn btn-primary&quot;&gt;Login&lt;/button&gt;
        &lt;div *ngIf=&quot;f.submitted &amp;&amp; isLoginFailed&quot; class=&quot;alert alert-danger&quot;&gt;
          Login failed:  { { errorMessage}}
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/form&gt;
    &lt;hr /&gt;
    &lt;p&gt;Don't have an account?&lt;/p&gt;
    &lt;a href=&quot;signup&quot; class=&quot;btn btn-success&quot;&gt;Sign Up&lt;/a&gt;
  &lt;/div&gt;
&lt;/ng-template&gt;</code></pre>
<h4>Register Component</h4>
<em>register.component.ts</em>
<pre><code class="language-javascript">
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;
      }
    );
  }
}</code></pre>
<em>register.component.html</em>
<pre><code class="language-html">&lt;div *ngIf=&quot;isSignedUp; else signupForm&quot;&gt;
  Your registration is successful. Please login!
&lt;/div&gt;

&lt;ng-template #signupForm&gt;
  &lt;div class=&quot;row col-sm-6&quot; style=&quot;max-width:350px;&quot;&gt;
    &lt;form name=&quot;form&quot; (ngSubmit)=&quot;f.form.valid &amp;&amp; onSubmit()&quot; #f=&quot;ngForm&quot; novalidate&gt;
      &lt;div class=&quot;form-group&quot;&gt;
        &lt;label for=&quot;name&quot;&gt;Your name&lt;/label&gt;
        &lt;input type=&quot;text&quot; class=&quot;form-control&quot; name=&quot;name&quot; [(ngModel)]=&quot;form.name&quot; #name=&quot;ngModel&quot; required /&gt;
        &lt;div *ngIf=&quot;f.submitted &amp;&amp; name.invalid&quot;&gt;
          &lt;div *ngIf=&quot;name.errors.required&quot;&gt;Name is required&lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
      &lt;div class=&quot;form-group&quot;&gt;
        &lt;label for=&quot;username&quot;&gt;Username&lt;/label&gt;
        &lt;input type=&quot;text&quot; class=&quot;form-control&quot; name=&quot;username&quot; [(ngModel)]=&quot;form.username&quot; #username=&quot;ngModel&quot;
          required /&gt;
        &lt;div *ngIf=&quot;f.submitted &amp;&amp; username.invalid&quot;&gt;
          &lt;div *ngIf=&quot;username.errors.required&quot;&gt;Username is required&lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
      &lt;div class=&quot;form-group&quot;&gt;
        &lt;label for=&quot;email&quot;&gt;Email&lt;/label&gt;
        &lt;input type=&quot;text&quot; class=&quot;form-control&quot; name=&quot;email&quot; [(ngModel)]=&quot;form.email&quot; #email=&quot;ngModel&quot; required email /&gt;
        &lt;div *ngIf=&quot;f.submitted &amp;&amp; email.invalid&quot;&gt;
          &lt;div *ngIf=&quot;email.errors.required&quot;&gt;Email is required&lt;/div&gt;
          &lt;div *ngIf=&quot;email.errors.email&quot;&gt;Email must be a valid email address&lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
      &lt;div class=&quot;form-group&quot;&gt;
        &lt;label for=&quot;password&quot;&gt;Password&lt;/label&gt;
        &lt;input type=&quot;password&quot; class=&quot;form-control&quot; name=&quot;password&quot; [(ngModel)]=&quot;form.password&quot; #password=&quot;ngModel&quot;
          required minlength=&quot;6&quot; /&gt;
        &lt;div *ngIf=&quot;f.submitted &amp;&amp; password.invalid&quot;&gt;
          &lt;div *ngIf=&quot;password.errors.required&quot;&gt;Password is required&lt;/div&gt;
          &lt;div *ngIf=&quot;password.errors.minlength&quot;&gt;Password must be at least 6 characters&lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
      &lt;div class=&quot;form-group&quot;&gt;
        &lt;button class=&quot;btn btn-primary&quot;&gt;Register&lt;/button&gt;
        &lt;div *ngIf=&quot;f.submitted &amp;&amp; isSignUpFailed&quot; class=&quot;alert alert-warning&quot;&gt;
          Signup failed!&lt;br/&gt; { { errorMessage}}
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/form&gt;
  &lt;/div&gt;
&lt;/ng-template&gt;</code></pre>
<h4>[UserRole] Components</h4>
<em>user/pm/admin.component.html</em>
<pre><code class="lang-html">&lt;h4&gt;Content from Server&lt;/h4&gt;
 { { board}}
 { { errorMessage}}
&lt;/pre&gt;
&lt;code&gt;user.component.ts&lt;/code&gt;
&lt;pre class=&quot;lang:java&quot;&gt;
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 =&gt; {
        this.board = data;
      },
      error =&gt; {
        this.errorMessage = `${error.status}: ${JSON.parse(error.error).message}`;
      }
    );
  }
}</code></pre>
<em>pm.component.ts</em>
<pre><code class="language-javascript">
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}`;
      }
    );
  }
}</code></pre>
<em>admin.component.ts</em>
<pre><code class="language-javascript">
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}`;
      }
    );
  }
}</code></pre>
<h3>App Routing</h3>
<em>app-routing.module.ts</em>
<pre><code class="language-javascript">
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 { }
</code></pre>
<em>app.component.ts</em>
<pre><code class="language-javascript">
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;
      });
    }
  }
}
</code></pre>
<em>app.component.html</em>
<pre><code class="language-javascript">&lt;nav class=&quot;navbar navbar-inverse&quot;&gt;
  &lt;div class=&quot;container-fluid&quot;&gt;
    &lt;div class=&quot;navbar-header&quot;&gt;
      &lt;a class=&quot;navbar-brand&quot; href=&quot;#&quot;&gt;grokonez&lt;/a&gt;
    &lt;/div&gt;
    &lt;ul class=&quot;nav navbar-nav&quot; routerLinkActive=&quot;active&quot;&gt;
      &lt;li class=&quot;nav-item&quot;&gt;&lt;a class=&quot;nav-link&quot; routerLink=&quot;home&quot;&gt;Home&lt;/a&gt;&lt;/li&gt;
      &lt;li *ngIf=&quot;authority === 'user'&quot; class=&quot;nav-item&quot;&gt;
        &lt;a class=&quot;nav-link&quot; routerLink=&quot;user&quot;&gt;User Board&lt;/a&gt;
      &lt;/li&gt;
      &lt;li *ngIf=&quot;authority === 'admin'&quot; class=&quot;nav-item&quot;&gt;
        &lt;a class=&quot;nav-link&quot; routerLink=&quot;admin&quot;&gt;Admin Board&lt;/a&gt;
      &lt;/li&gt;
      &lt;li *ngIf=&quot;authority === 'pm'&quot; class=&quot;nav-item&quot;&gt;
        &lt;a class=&quot;nav-link&quot; routerLink=&quot;pm&quot;&gt;PM Board&lt;/a&gt;
      &lt;/li&gt;
      &lt;li *ngIf=&quot;!authority&quot; class=&quot;nav-item&quot;&gt;
        &lt;a class=&quot;nav-link&quot; routerLink=&quot;auth/login&quot;&gt;Login&lt;/a&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/div&gt;
&lt;/nav&gt;
&lt;div class=&quot;container&quot;&gt;
  &lt;router-outlet&gt;&lt;/router-outlet&gt;
&lt;/div&gt;</code></pre>
<h2>SourceCode</h2>

<a href="https://grokonez.com/wp-content/uploads/2018/10/AngularJwtAuth.zip">AngularJwtAuth</a>


https://grokonez.com/frontend/angular/angular-6/angular-spring-boot-jwt-authentication-example-angular-6-spring-security-mysql-full-stack-part-3-build-frontend