Monday, February 8, 2021

Angular + Spring Boot JWT Authentication Example

In this tutorial we’ll create a full login example with authentication using Angular, Spring Boot, Spring Security, JWT authentication.

In the back end Spring Boot is used to create REST APIs, Spring security is used for authentication and authorization, token based authentication is done using JWT, Spring Data JPA is used for DB operations.

In the front end Angular is used to create forms, provide routing and Bootstrap is used for styling.

Back end Spring Boot service

In the backend we have a Spring Boot application that uses Spring Security and JWT token based authentication to bring authentication and authorization to the exposed REST APIs. DB used is MySQL.

To get more details and code for the Spring Boot + Spring Security JWT Authentication Example refer this post- https://www.netjstech.com/2021/02/spring-boot-spring-security-jwt-authentication.html

Only change that is required in the Spring boot application is to add @CrossOrigin annotation in the Controller class to enable cross-origin resource sharing (CORS). If you won't use this annotation cross browser HTTP request won't be allowed and the Spring Boot application won't accept any request coming from http://localhost:4200

@RestController
@CrossOrigin(origins="http://localhost:4200") 
@RequestMapping("/auth")
public class AuthController {
  @Autowired
  UserRepository userRepository;
  @Autowired
  RoleRepository roleRepository;
  @Autowired
  PasswordEncoder encoder;
  @Autowired
  AuthenticationManager authenticationManager;
  @Autowired
  JwtTokenUtil jwtTokenUtil;
  
  @PostMapping("/login")
  public ResponseEntity<?> userLogin(@Valid @RequestBody User user) {
    System.out.println("AuthController -- userLogin");
    Authentication authentication = authenticationManager.authenticate(
          new UsernamePasswordAuthenticationToken(user.getUserName(), user.getPassword()));
    
    SecurityContextHolder.getContext().setAuthentication(authentication);
    String token = jwtTokenUtil.generateJwtToken(authentication);
    CustomUserBean userBean = (CustomUserBean) authentication.getPrincipal();    
    List<String> roles = userBean.getAuthorities().stream()
                   .map(auth -> auth.getAuthority())
                   .collect(Collectors.toList());
    AuthResponse authResponse = new AuthResponse();
    authResponse.setToken(token);
    authResponse.setRoles(roles);
    return ResponseEntity.ok(authResponse);
  }
  
  @PostMapping("/signup")
  public ResponseEntity<?> userSignup(@Valid @RequestBody SignupRequest signupRequest) {
    if(userRepository.existsByUserName(signupRequest.getUserName())){
      return ResponseEntity.badRequest().body("Username is already taken");
    }
    if(userRepository.existsByEmail(signupRequest.getEmail())){
      return ResponseEntity.badRequest().body("Email is already taken");
    }
    User user = new User();
    Set<Role> roles = new HashSet<>();
    user.setUserName(signupRequest.getUserName());
    user.setEmail(signupRequest.getEmail());
    user.setPassword(encoder.encode(signupRequest.getPassword()));
    //System.out.println("Encoded password--- " + user.getPassword());
    String[] roleArr = signupRequest.getRoles();
    
    if(roleArr == null) {
      roles.add(roleRepository.findByRoleName(Roles.ROLE_USER).get());
    }
    for(String role: roleArr) {
      switch(role.toLowerCase()) {
        case "admin":
          roles.add(roleRepository.findByRoleName(Roles.ROLE_ADMIN).get());
          break;
        case "user":
          roles.add(roleRepository.findByRoleName(Roles.ROLE_USER).get());
          break;  
        default:
          return ResponseEntity.badRequest().body("Specified role not found");
      }
    }
    user.setRoles(roles);
    userRepository.save(user);
    return ResponseEntity.ok("User signed up successfully");
  }
}
@RestController
@CrossOrigin(origins="*")
@RequestMapping("/user")
public class UserController {
  @GetMapping("/allusers")
  public String displayUsers() {
    return "Display All Users";
  }
  
  @GetMapping("/displayuser")
  @PreAuthorize("hasRole('ROLE_USER') or hasRole('ROLE_ADMIN')")
  public String displayToUser() {
    return "Display to both user and admin";
  }
  
  @GetMapping("/displayadmin")
  @PreAuthorize("hasRole('ROLE_ADMIN')")
  public String displayToAdmin() {
    return "Display only to admin";
  }
}

Angular and Spring Boot login with authentication example

In Angular application we’ll have to create forms for registering a user and for login. We’ll also create components to see how to access content that doesn’t need authentication and how to access content that needs both authentication and authorization.

Forms for signup and login should look as given below-

Angular user registration example
Angular user login example

Home page just displays some information about the logged user and All Users menu option displays the content that can be accessed with out logging in.

After user is logged in

Login with token Angular

On the right hand side logged in user name is displayed and LogOut option is displayed instead of Login. After successful login user is routed to the home page displaying user information. More menu options are also displayed for the logged in user.

Model class

There is a model class User with fields userName, email, password, roles.

user.model.ts

export class User{
    userName: string;
    email: string;
    password: string;
    roles: string[];
    constructor(userName: string, email: string, password: string, roles: string[]) {
        this.userName = userName;
        this.email = email;
        this.password = password;
        this.roles  = roles;
    }
}

SignUp Component

There is a signup form for user registration, that is written as a reactive form.

register.component.ts

import { Component, OnInit } from "@angular/core";
import { FormArray, FormControl, FormGroup, Validators } from "@angular/forms";
import { AuthService } from "src/app/services/auth.service";
import { User } from "../user.model";

@Component({
    selector: 'app-register',
    templateUrl: './register.component.html', 
})
export class RegisterComponent implements OnInit{
    registrationForm: FormGroup;
    user = new User('', '', '', []);
    isRegistered = false;
    submitted = false;
    errorMessage = '';
    roles: any = [
        {name:'User', id:1, selected: true}, 
        {name:'Admin', id:2, selected: false},
    ]
    selectedRoles: string[];
    constructor(private authService: AuthService){ }
    ngOnInit() {
        this.registrationForm = new FormGroup({
            userName: new FormControl(null, [Validators.required, Validators.minLength(5), Validators.maxLength(20)]),
            email: new FormControl(null, [Validators.required, Validators.email]),
            password: new FormControl(null, [Validators.required, Validators.minLength(8)]),
            roleSelection: this.createRoles(this.roles)
        });
    }
      // Create form array
    createRoles(rolesList): FormArray{
        const arr = rolesList.map(role => {
        return new FormControl(role.selected)
        });
        return new FormArray(arr);
    }
    onSubmit(){
        this.submitted = true;
        this.user.userName = this.registrationForm.value.userName;
        this.user.email = this.registrationForm.value.email;
        this.user.password = this.registrationForm.value.password;
        //console.log(this.getSelectedRoles());
        this.user.roles = this.getSelectedRoles();
        this.registerUser()
    }
    registerUser(){
        this.authService.signup(this.user)
        .subscribe(user=> {
            console.log(user);
            this.isRegistered = true;
        }, error=> {
            console.log(error);
            this.errorMessage = error;
            this.isRegistered = false;
        });
    }

    getSelectedRoles():string[]  {
        this.selectedRoles = this.registrationForm.value.roleSelection.map((selected, i) => {
          if(selected){
            return this.roles[i].name;
          }else {
            return '';
          }
        });
        // return selected roles
        return this.selectedRoles.filter(function (element) {
          if (element !== '') {
            return element;
          }
        });
      }
}

Important points to note here-

  1. This reactive form has form controls for entering userName, email, password and a dropdown for selecting roles.
  2. Note that here roles is put in the registration form itself but logically it is not the right place to do that. Assigning roles to the user should be done by admin. Here the code is shown just for demo purpose.
  3. There is also validation for the fields.
  4. Once the form is submitted an object of type User is created with the entered values. Passing that user object signUp() method of the AuthService is called to save the user data.

Template (register.component.html)

<div class="col-xs-12 col-sm-10 col-md-8">    
  <div class="card">
    <div class="card-header">
      <h2>User Signup</h2>
    </div>
    <div class="card-body">
      <form [formGroup]="registrationForm" (ngSubmit)="onSubmit()">
        <div class="form-group">
          <label for="userName">User Name</label>
          <input type="text" class="form-control" id="userName"
            formControlName="userName">       
          <div class="alert alert-danger" *ngIf="registrationForm.get('userName').invalid 
            && registrationForm.get('userName').touched">
            <div *ngIf="registrationForm.get('userName').errors.required">
              User name is required
            </div>
            <div *ngIf="registrationForm.get('userName').errors.minlength">
              User name must be at least 5 characters long
            </div>
            <div *ngIf="registrationForm.get('userName').errors.maxlength">
              User name should not be more than 20 characters long
            </div>
          </div>                      
        </div>
        <div class="form-group">
          <label for="email">email</label>
          <input type="email" class="form-control" id="email"
          formControlName="email">  
          <div class="alert alert-danger" *ngIf="registrationForm.get('email').invalid 
              && registrationForm.get('email').touched">
            Please enter a valid email
          </div>                      
        </div>
        <div class="form-group">
          <label for="password">Password</label>
          <input type="password" class="form-control" id="password"
          formControlName="password"> 
          <div class="alert alert-danger" *ngIf="registrationForm.get('password').invalid 
            && registrationForm.get('password').touched">
            <div *ngIf="registrationForm.get('password').errors.required">
              Password is required
            </div>
            <div *ngIf="registrationForm.get('password').errors.minlength">
              Password must be at least 8 characters long
            </div>
          </div>                       
        </div>
        <div class="form-group">
          <label>User Roles</label>
            <div class="form-check" 
            *ngFor="let role of registrationForm.get('roleSelection')['controls']; let i = index">
              <label class="form-check-label">
                  <input type="checkbox" class="form-check-input"
                  [formControl]="role">
                  {{roles[i].name}}
              </label>
            </div>
        </div>
        <button type="submit" [disabled]="registrationForm.invalid" class="btn btn-success">Sign Up</button>
        <div class="alert alert-warning" *ngIf="submitted && !isRegistered">
          Signup failed- {{ errorMessage }}
        </div>
      </form> 
      <div class="alert alert-success" *ngIf="isRegistered">
        Your registration is successful!
      </div>
    </div>
  </div>
</div>

Login Component

There is another reactive form for user login.

login.component.ts

import { Component, OnInit } from "@angular/core";
import { FormControl, FormGroup, Validators } from "@angular/forms";
import { Router } from "@angular/router";
import { AuthService } from "src/app/services/auth.service";

@Component({
    selector: 'app-login',
    templateUrl: './login.component.html', 
})
export class LoginComponent implements OnInit{
    loginForm: FormGroup;
    constructor(private authService: AuthService, private router: Router){ }
    submitted = false;
    errorMessage = '';
    isLoggedin = false;
    isLoginFailed = false;
    ngOnInit() {
        this.loginForm = new FormGroup({
            userName: new FormControl(null, Validators.required),
            password: new FormControl(null, Validators.required),
        });
    }
    onSubmit(){
        this.submitted = true;
        this.authService.login(this.loginForm.value.userName, this.loginForm.value.password).subscribe(
            data=>{
                this.isLoggedin = true
                this.router.navigate(['/home']);
            },
            error=>{
                console.log(error);
                this.errorMessage = error;
                this.isLoggedin = false;
                this.isLoginFailed = true;
            }
        );
    }
}

Important points to note here-

  1. This reactive form has form controls for entering userName and password. Both are defined as required field.
  2. Once the form is submitted login() method of the AuthService is called with user name and password as argument.
  3. If login is successful user is navigated to home page.

Template (login.component.html)

<div class="col-xs-12 col-sm-10 col-md-8">    
  <div class="card">
    <div class="card-header">
      <h2>User Login</h2>
    </div>
    <div class="card-body">
      <form [formGroup]="loginForm" (ngSubmit)="onSubmit()">
        <div class="form-group">
          <label for="userName">User Name</label>
          <input type="text" class="form-control" id="userName"
            formControlName="userName">       
          <div class="alert alert-danger" *ngIf="loginForm.get('userName').invalid 
            && loginForm.get('userName').touched">
            <div *ngIf="loginForm.get('userName').errors.required">
              User name is required
            </div>
          </div>                      
        </div>
        <div class="form-group">
          <label for="password">Password</label>
          <input type="password" class="form-control" id="password"
          formControlName="password"> 
          <div class="alert alert-danger" *ngIf="loginForm.get('password').invalid 
            && loginForm.get('password').touched">
            <div *ngIf="loginForm.get('password').errors.required">
              Password is required
            </div>
          </div>                       
        </div>
        <button type="submit" [disabled]="loginForm.invalid" class="btn btn-success">Login</button>
        <div class="alert alert-warning" *ngIf="submitted && isLoginFailed">
          Login failed- {{ errorMessage }}
        </div>
      </form> 
    </div>
  </div>
</div>

home.component.ts

If the login is successful user navigates to this component where user information is displayed.

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

@Component({
    selector: 'app-home',
    templateUrl: './home.component.html'
})
export class HomeComponent implements OnInit {
    userName: string;
    userRoles: string;
    constructor() { }
    ngOnInit() {
      this.userName = sessionStorage.getItem("username");
      this.userRoles = sessionStorage.getItem("roles");
    }
}

home.component.html

<div class="container">
    <div class="row">
        <p> {{userName}} logged in with {{userRoles}}</p>
    </div>
</div>

Authentication Service

This service class has the functionality to send registration and login request and accept response.

auth.service.ts

import { Injectable } from '@angular/core';
import { Observable, throwError } from 'rxjs';
import { User } from '../auth/user.model';
import { HttpClient, HttpErrorResponse, HttpHeaders } from '@angular/common/http';
import { catchError, map } from 'rxjs/operators';
import { Router } from '@angular/router';

const headers = new HttpHeaders().set('Content-Type', 'application/json');
@Injectable({
  providedIn: 'root'
})
export class AuthService{
  private baseUrl = 'http://localhost:8080/auth/'; 

  constructor(private http: HttpClient,  private router: Router){}
  signup(user: User): Observable<any>{
    //console.log('In AuthService');
    return this.http.post(this.baseUrl + 'signup', user, { headers, responseType: 'text'})
                    .pipe(catchError(this.handleError));
  }
  login(user: string, password: string){
    // console.log('In AuthService -  login');
    return this.http.post<any>(this.baseUrl + 'login', 
      {userName: user, password:password}, {headers})
      .pipe(catchError(this.handleError),
        map(userData => {
          sessionStorage.setItem("username", user);
          let tokenStr = "Bearer " + userData.token;
          console.log("Token---  " + tokenStr);
          sessionStorage.setItem("token", tokenStr);
          sessionStorage.setItem("roles", JSON.stringify(userData.roles));
          return userData;
        })
      ); 
  }

  logout(){
    sessionStorage.clear()
    this.router.navigate(['/login']);
  }

  isLoggedIn(): boolean{
    return sessionStorage.getItem('username') !== null;
  }

  private handleError(httpError: HttpErrorResponse) {
    let message:string = '';

    if (httpError.error instanceof ProgressEvent) {
      console.log('in progrss event')
      message = "Network error";
    }
    else {
      message = httpError.error.message;
      // The backend returned an unsuccessful response code.
      // The response body may contain clues as to what went wrong.
      console.error(
        `Backend returned code ${httpError.status}, ` +
        `body was: ${httpError.error}`);
    }
    // Return an observable with a user-facing error message.
    return throwError(message);
  }
}

Important points to note here are-

  1. Instance of HttpClient is injected in Service class. HttpClient class supports HTTP methods such as PUT, POST, GET and DELETE.
  2. We are making a request to the AuthController in the Spring Boot application which handles any request having path as ‘/auth’ so the base url is assigned as 'http://localhost:8080/auth/’
  3. In the signup() method User object is passed as the body of the request through the post method.
  4. In the login() method you get back the generated token and user roles as response which is stored in sessionStorage. User name is also stored in sessionStorage.
  5. logout() method clears the sessionStorage and navigates back to login page.
  6. handleError() method in the Service class is used to check the returned error. Error handler also returns an RxJS ErrorObservable (throwError) with a user-friendly error message.

AuthInterceptor class

Once user is authenticated you need to send the generated token with each request. For that a class that implements HttpInterceptor is used. Most interceptors transform the outgoing request before passing it to the next interceptor in the chain, by calling next.handle(transformedReq)

import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from "@angular/common/http";
import { Injectable } from "@angular/core";
import { Observable } from "rxjs";

@Injectable()
export class AuthInterceptor implements HttpInterceptor{
    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {       
        let modifiedReq = req
        if (sessionStorage.getItem('username') && sessionStorage.getItem('token')) {
            //console.log('With Token --- ' + sessionStorage.getItem('token'));
            modifiedReq = req.clone({
                setHeaders: {
                    Authorization: sessionStorage.getItem('token')
                  }
            });
        }
        return next.handle(modifiedReq);
    }
}

If user is logged in (checked by verifying the values in sessionStorage) you set ‘Authorization’ and bearer token as key, Value pair in the request header.

app.component.ts

This is the root component in the Angular application and has code to show the menu options.

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  constructor(private authService: AuthService){}
  //isLoggedIn = false;
  userName: string = '';
  ngOnInit(): void {
    //this.isLoggedIn = this.authService.isLoggedIn();
  }
  getUserName(){
     return sessionStorage.getItem("username");
  }
  onLogOut(){
    this.authService.logout();
  }

  loggedIn(){
    return this.authService.isLoggedIn()
  }
}

app.component.html

In the template there are router links and the logic when to show those links or when to change the link. When user is not logged in “SignUp” and “Login” links are displayed. Once user logs in these links change to Username and LogOut.

When the user logs in links to show user and admin content are also displayed.

<nav class="navbar navbar-expand-md bg-dark navbar-dark">
  <div class="container-fluid">
    <div class="collapse navbar-collapse" id="collapsibleNavbar">
      <ul class="nav navbar-nav">
        <li class="nav-item" routerLinkActive="active">
          <a class="nav-link" routerLink="/home">Home</a>
        </li>
        <li class="nav-item" routerLinkActive="active">
          <a class="nav-link" routerLink="/allusers">All Users</a>
        </li>
        <li class="nav-item" routerLinkActive="active">
          <a class="nav-link" *ngIf="loggedIn()" routerLink="/usercontent">User Content</a>
        </li>
        <li class="nav-item" routerLinkActive="active">
          <a class="nav-link" *ngIf="loggedIn()" routerLink="/admincontent">Admin Content</a>
        </li>
      </ul>

      <ul class="navbar-nav ml-auto">
        <li class="nav-item">
          <a href="/signup" class="nav-link" routerLink="signup" *ngIf="!loggedIn()">Sign Up</a>
        </li>
        <li class="nav-item">
          <a href="/login" class="nav-link" routerLink="login" *ngIf="!loggedIn()">Login</a>
        </li>
        <li class="nav-item">
          <a href class="nav-link" *ngIf="loggedIn()">{{ getUserName() }}</a>
        </li>
        <li class="nav-item">
          <a href class="nav-link" (click)="onLogOut()" *ngIf="loggedIn()" routerLink="logout">LogOut</a>
        </li>
      </ul>
    </div>
  </div>
</nav>
<div class="container">
  <div class="row"><p></p></div>
  <div class="row">
    <div class="col-md-12">
      <router-outlet></router-outlet>
    </div>
  </div>
</div>

Authorization Related classes

Above part covers the authentication part we’ll have some more classes to demonstrate authorization. There will be three components to show how public content can be accessed without any authentication and components to show how content can be accessed only by the user that is authorized (has the assigned role) to access it.

user.service.ts

This class has methods to make requests to the methods in UserController class in Spring Boot application.

import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';

@Injectable({
    providedIn: 'root',
})
export class UserService {
    private baseUrl = 'http://localhost:8080/user/'; 
    constructor(private http: HttpClient){}
    getAllUsers(): Observable<any>{
        return this.http.get(this.baseUrl+'allusers', {responseType:'text'})
                        .pipe(catchError(this.handleError));
    }
    getByUserRole():  Observable<any>{
      return this.http.get(this.baseUrl+'displayuser', {responseType:'text'})
                      .pipe(catchError(this.handleError));
    }
    getByAdminRole():  Observable<any>{
      return this.http.get(this.baseUrl+'displayadmin', {responseType:'text'})
                      .pipe(catchError(this.handleError));
    }

    private handleError(httpError: HttpErrorResponse) {
      let message:string = '';
      if (httpError.error instanceof ProgressEvent) {
        console.log('in progrss event')
        message = "Network error";
      }
      else {
        message = JSON.parse(httpError.error).message;
        // The backend returned an unsuccessful response code.
        // The response body may contain clues as to what went wrong.
        console.error(           
          `Backend returned code ${httpError.status}, ` +
          `body was: ${httpError.error}`);
    }
    // Return an observable with a user-facing error message.
    return throwError(
      'Something bad happened; please try again later. Error Message- ' + message);
    }
}

AllUsersComponent

This component is used to show public content.

allusers.component.ts

import { Component, OnInit } from '@angular/core';
import { UserService } from '../services/user.service';
@Component({
    selector: 'app-allusers',
    templateUrl: './allusers.component.html',
})
export class AllUsersComponent implements OnInit{
    content: string;
    constructor(private userService: UserService) { }
    ngOnInit(): void {
        this.userService.getAllUsers().subscribe(
            data => {
              this.content = data;
            },
            error => {
              this.content = error;
            }
        );
    }
}

allusers.component.html

<div class="container">  
    <p>{{ content }}</p>
</div>

UserContent

This component is used to show content that can only be accessed by somebody with assigned role as user or admin.

usercontent.component.ts

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

@Component({
    selector: 'app-usercontent',
    templateUrl: './usercontent.component.html',
})
export class UserContent implements OnInit{
    content: string;
    constructor(private userService: UserService) { }
    ngOnInit(): void {
        this.userService.getByUserRole().subscribe(
            data => {
                this.content = data;
            },
            error => {
                this.content = error;
            }
        );
    }
}

usercontent.component.html

<div class="container">  
    <p>{{ content }}</p>
</div>

AdminContent

This component is used to show content that can only be accessed by somebody with assigned role as admin.

admincontent.component.ts

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

@Component({
    selector: 'app-admincontent',
    templateUrl: './admincontent.component.html',
})
export class AdminContent implements OnInit{
    content: string;
    constructor(private userService: UserService) { }
    ngOnInit(): void {
        this.userService.getByAdminRole().subscribe(
            data => {
                this.content = data;
            },
            error => {
                this.content = error;
            }
        );
    }
}

admincontent.component.html

<div class="container">  
    <p>{{ content }}</p>
</div>

AppRoutingModule

Module for angular routing configuration.

app-routing.module.ts

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { RegisterComponent } from './auth/register/register.component';
import { HomeComponent } from './home.component';
import { LoginComponent } from './auth/login/login.component';
import { AllUsersComponent } from './users/allusers.component';
import { UserContent } from './users/usercontent.component';
import { AdminContent } from './users/admincontent.component';

const routes: Routes = [
    {path: 'signup', component: RegisterComponent}, 
    {path: 'login', component: LoginComponent}, 
    {path: 'home', component: HomeComponent},
    {path: 'allusers', component: AllUsersComponent},
    {path: 'usercontent', component: UserContent},
    {path: 'admincontent', component: AdminContent}          
];

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

AppModule

This is the module where you will declare all the components, interceptors and other modules that are used in this Angular application.

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HttpClientModule, HTTP_INTERCEPTORS} from '@angular/common/http';
import { AppRoutingModule } from './app-routing.module';
import { RegisterComponent } from './auth/register/register.component';
import { LoginComponent } from './auth/login/login.component';
import { HomeComponent } from './home.component';
import { AllUsersComponent } from './users/allusers.component';
import { AuthInterceptor } from './services/authintercptor.service';

@NgModule({
  declarations: [
    AppComponent,
    RegisterComponent,
    LoginComponent,
    HomeComponent, 
    AllUsersComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    ReactiveFormsModule,
    AppRoutingModule, 
    HttpClientModule
  ],
  providers: [{provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true}],
  bootstrap: [AppComponent]
})
export class AppModule { }

src/assets/forms.css

This CSS class is used to show a green or red bar on the left side of the form controls.

.ng-invalid.ng-untouched:not(form)  {
  border-left: 5px solid #42A948; /* green */
}

.ng-invalid.ng-touched:not(form)  {
  border-left: 5px solid #a94442; /* red */
}

In the index.html file, update the <head> tag to include the new style sheet.

<link rel="stylesheet" href="assets/forms.css">

User with admin access

Angular app with authorization

User not having admin access

Angular app

That's all for this topic Angular + Spring Boot JWT Authentication Example. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related Topics

  1. Angular HttpClient - Set Response Type as Text
  2. Angular Routing Concepts With Example
  3. How to Create a Custom Observable in Angular
  4. Angular Reactive Form Validation Example
  5. Angular Template-Driven Form Validation Example

You may also like-

  1. Angular One-Way Data Binding Using String Interpolation
  2. Angular Two-Way Data Binding With Examples
  3. Pure and Impure Pipes in Angular
  4. CanDeactivate Guard in Angular With Example
  5. Parallel Stream in Java Stream API
  6. Array in Java With Examples
  7. @Import Annotation in Spring JavaConfig
  8. Spring MVC - Binding List of Objects Example

No comments:

Post a Comment