Angular Spring Boot JWT Authentication example
angular
45
springboot
48
JWT
9
authentication
14
Male avatar

loveprogramming viết ngày 16/03/2021

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 Part 3 of the series: Angular Spring Boot JWT Authentication example | Angular 6 + 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:

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 6

  • 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="#">grokonez</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

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

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

Male avatar

loveprogramming

220 bài viết.
64 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
Male avatar
1 0
Tutorial Link: (Link) (Ảnh) Django is a Pythonbased free and opensource web framework that follows the modeltemplateview architectural pattern. A...
loveprogramming viết 5 tháng trước
1 0
Male avatar
1 0
https://loizenai.com/angular11nodejspostgresqlcrudexample/ Angular 11 Node.js PostgreSQL Crud Example (Ảnh) Tutorial: “Angular 11 Node.js Postg...
loveprogramming viết 4 tháng trước
1 0
Male avatar
1 0
Angular Spring Boot jwt Authentication Example Github https://loizenai.com/angularspringbootjwt/ (Ảnh) Tutorial: ” Angular Spring Boot jwt Authe...
loveprogramming viết 4 tháng trước
1 0
Bài viết liên quan
Male avatar
0 0
https://grokonez.com/frontend/angular/angular6/kotlinspringbootangular6crudhttpclientmysqlexamplespringdatajparestapisexample Kotlin Spring Boot +...
loveprogramming viết 8 ngày trước
0 0
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


Male avatar
{{userFollowed ? 'Following' : 'Follow'}}
220 bài viết.
64 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!