Securing Angular 7 application with Spring Boot JWT Security

As we have secured our angular application using spring boot basic security. In this article , i will take you through all the steps to secure your angular application with spring boot JWT(JSON Web Token) security. Using Spring boot basic security , we can provide in token based authentication and authorization to our application.

We need to configure users and their roles in in separate configuration file. We are creating two projects here –

  1. Spring boot JWT security application – This will handle the authentication and authorization for our UI application.
  2. UI for Todo manager in angular – This is the Todo manager application which will leverage the spring boot security feature.

Part 1: Spring boot JWT security application

In this part , we will be creating a spring boot application which provides JWT security. Its basically a RESTful webservice which has endpoints to access and modify Todos information. To access the Todo information through API , user needs to authenticate himself. Below configuration enables basic security.

SecurityConfig.java

package com.myjavablog.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

	@Autowired
	private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;

	@Autowired
	private UserDetailsService jwtUserDetailsService;

	@Autowired
	private JwtRequestFilter jwtRequestFilter;

	@Autowired
	public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
		// configure AuthenticationManager so that it knows from where to load
		// user for matching credentials
		// Use BCryptPasswordEncoder
		auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder());
	}

	@Bean
	public PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}

	@Bean
	@Override
	public AuthenticationManager authenticationManagerBean() throws Exception {
		return super.authenticationManagerBean();
	}

	@Override
	protected void configure(HttpSecurity httpSecurity) throws Exception {
		// We don't need CSRF for this example
		httpSecurity.csrf().disable()
				// dont authenticate this particular request
				.authorizeRequests().antMatchers("/authenticate").permitAll().antMatchers(HttpMethod.OPTIONS, "/**")
				.permitAll().
				// all other requests need to be authenticated
				anyRequest().authenticated().and().
				// make sure we use stateless session; session won't be used to
				// store user's state.
				exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement()
				.sessionCreationPolicy(SessionCreationPolicy.STATELESS);

		// Add a filter to validate the tokens with every request
		httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
	}
}

JwtAuthenticationController.java

Below is JwtAuthenticationController.java which has API endpoint to authenticate the user and provides us with JWT. createAuthenticationToken() method validates the user and generated the token for us which has expiration of 5*60*60 (5 Hours).

package com.myjavablog.controller;

import java.util.Objects;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.myjavablog.config.JwtTokenUtil;
import com.myjavablog.model.JwtRequest;
import com.myjavablog.model.JwtResponse;

@RestController
@CrossOrigin
public class JwtAuthenticationController {

	@Autowired
	private AuthenticationManager authenticationManager;

	@Autowired
	private JwtTokenUtil jwtTokenUtil;

	@Autowired
	private UserDetailsService jwtInMemoryUserDetailsService;

	@RequestMapping(value = "/authenticate", method = RequestMethod.POST)
	public ResponseEntity<?> createAuthenticationToken(@RequestBody JwtRequest authenticationRequest)
			throws Exception {

		authenticate(authenticationRequest.getUsername(), authenticationRequest.getPassword());

		final UserDetails userDetails = jwtInMemoryUserDetailsService
				.loadUserByUsername(authenticationRequest.getUsername());

		final String token = jwtTokenUtil.generateToken(userDetails);

		return ResponseEntity.ok(new JwtResponse(token));
	}

	private void authenticate(String username, String password) throws Exception {
		Objects.requireNonNull(username);
		Objects.requireNonNull(password);

		try {
			authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
		} catch (DisabledException e) {
			throw new Exception("USER_DISABLED", e);
		} catch (BadCredentialsException e) {
			throw new Exception("INVALID_CREDENTIALS", e);
		}
	}
}

TodoController.java

Below is the Rest controller class for managing Todo’s information –

package com.myjavablog.controller;

import com.myjavablog.model.Todo;
import com.myjavablog.model.User;
import com.myjavablog.repository.TodoRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;

@CrossOrigin(origins = "http://localhost:4200")
@RestController
@RequestMapping("/api")
public class TodoController {

    //@Autowired
    //private TodoRepository todoRepository;

    static List<Todo> list = new ArrayList<>();

    @GetMapping(produces = "application/json")
    @RequestMapping({ "/validateLogin" })
    public User validateLogin()
    {
        return new User("User successfully authenticated");
    }

    @GetMapping(produces = "application/json")
    @RequestMapping("/todos")
    public List<Todo> getTodos(){
        return list;
    }

    TodoController(){

        list.add(new Todo(1l,"Badminton", "Badminton at 6 am", "Pending"));
        list.add(new Todo(2l,"Cricket", "Cricket at 7 am", "Pending"));
        list.add(new Todo(3l,"Football", "Football at 8 am", "Pending"));
        list.add(new Todo(4l,"Cards", "Cards at 6 am", "Pending"));
        list.add(new Todo(5l,"TT", "TT at 6 am", "Completed"));
        list.add(new Todo(6l,"Golf", "Golf at 6 am", "Pending"));
        list.add(new Todo(7l,"Running", "Running at 6 am", "Pending"));
        list.add(new Todo(8l,"Walking", "Walking at 6 am", "Pending"));
        list.add(new Todo(9l,"Swimming", "Swimming at 6 am", "Completed"));
        list.add(new Todo(10l,"Reading", "Reading at 6 am", "Pending"));

        //return list;
    }

    @GetMapping("/todo/{id}")
    public Todo getTodo(@PathVariable Long id)
    {
        //return todoRepository.findById(id);

        for(Todo todo : list){

            if(todo.getId() == id){
                return todo;
            }
        }

        return null;
    }

    @DeleteMapping("/todo/{id}")
    public boolean deleteTodo(@PathVariable Long id){
        //todoRepository.deleteById(id);

        for(Todo todo : list){

            if(todo.getId() == id){
                list.remove(todo);
            }
        }

        return true;
    }

    @PostMapping("/todo")
    public Todo createTodo(@RequestBody Todo todo){

        list.add(todo);
        return todo;
        //return todoRepository.save(todo);
    }

    @PutMapping("/todo")
    public Todo updateTodo(@RequestBody Todo todo){


        for(Todo t : list){

            if(todo.getId() == t.getId()){
                list.remove(t);
                list.add(todo);
                return todo;
            }
        }

        return null;
        //return todoRepository.save(todo);
    }
}

JwtTokenUtil.java – This class is responsible for token generation.

JwtRequestFilter.java – All request to REST API are filtered through this class. Token is validated in each request for user authorization.

We can call the webservice through POSTMAN as below –

Get the Authentication Token
Call to todo API using Authentication token

Source code can be downloaded from below link –

Part 2: UI for Todo manager in angular 7

All the REST service calls are authenticated and authorized using the spring boot JWT security. Before calling todo services , we need to pass authorization header with all requests from UI.

In JWT token based security , we pass on base 64 encoded string of username and password to server as authorization header in below format –

Authorization Header = “Bearer ” + Base64Encoder(username:password)

Step 1: Create new angular application

Refer below post for creating new angular application –

Angular Installation and Setup

Step 2: Create modules for material and primeng components

Create two modules for material(material.module.ts) and primeng( primeng.module.ts ) to define all the related components required in application. These two modules are also required to be imported in main module (app.module.ts)

Please refer below link for primeng and material configuration –

CRUD Operations using primeng datatable and Material Design

Step 3: Create components (Presentation layer)

Create components named header, footer, login,logout and todo components for Todo Manager application.

Todo manager application shows all todos in primeng datatable . Also we are allowed to perform CRUD operations on all todos. All CRUD operations are done in todo component as below –

Todo component –

  • components\todo\todo.component.html
  • components\todo\todo.component.ts

Step 4: Add the routing for application (Application Navigation/Routing)

We have to create a route for the application navigation. Below file contains routing configuration –

  • src\app\app-routing.module.ts
import { NgModule, ViewChildren } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { TodoComponent } from './components/todo/todo.component';
import { TodoAddComponent } from './components/todo-add/todo-add.component';
import { TodoEditComponent } from './components/todo-edit/todo-edit.component';
import { AuthGaurdService } from './services/auth-gaurd.service';
import { LogoutComponent } from './components/logout/logout.component';
import { LoginComponent } from './components/login/login.component';

const routes: Routes = [
  { path: 'todo-list', component: TodoComponent ,canActivate:[AuthGaurdService]},
  { path: 'todo-add', component: TodoAddComponent ,canActivate:[AuthGaurdService] },
  { path: 'todo-edit/:id', component: TodoEditComponent ,canActivate:[AuthGaurdService]},
  { path: '', component: TodoComponent,canActivate:[AuthGaurdService] },
  { path: 'login', component: LoginComponent },
  { path: 'logout', component: LogoutComponent,canActivate:[AuthGaurdService] }
];

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

All the routes are guarded by auth guard service. AuthGuardService checks ,if user is logged in to the application and then only it activates those routes configured in app-routing.module.ts .

AuthGuardService has canActivate() method which returns true if user is already logged into the application. If user is not logged into the application then it returns false and redirect him to login page.

import { Injectable } from '@angular/core';
import { Router, ActivatedRouteSnapshot, RouterStateSnapshot, CanActivate } from '@angular/router';
import { AuthenticationService } from './authentication.service';

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

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

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
    if (this.authService.isUserLoggedIn())
      return true;

    this.router.navigate(['login']);
    return false;
  }
}

Step 5: Authentication and Todo service (Service Layer)

AuthenticationService (authentication.service.ts) – It handles user authentication into the application. It has authenticate( username, password ) method which receives username and password entered on login page and authenticate user using spring basic authentication method.

Whenever you hit webservice , SecurityConfig.java comes into picture. If you remember, we have configured JWT security in spring boot application created earlier . When we hit todo service then every the time we are authenticated against spring boot JWT security. We need to pass authorization header all the time while hitting webservice from UI. Authorization header contains user credentials and JSON Web Token which has some expiry value upto which we can make request to TODO API.

Also it stores user information in session for checking user logged in status.

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

export class User{
  constructor(
    public status:string,
     ) {}
  
}

export class JwtResponse{
  constructor(
    public jwttoken:string,
     ) {}
  
}

@Injectable({
  providedIn: 'root'
})
export class AuthenticationService {
  
  constructor(private httpClient: HttpClient){
  }

  authenticate(username, password) {
    return this.httpClient.post<any>('http://localhost:8081/authenticate',{username,password}).pipe(
     map(
       userData => {
        sessionStorage.setItem('username',username);
        let tokenStr= 'Bearer '+userData.token;
        sessionStorage.setItem('token', tokenStr);
        return userData;
       }
     )

    );
  }

  isUserLoggedIn() {
    let user = sessionStorage.getItem('username')
    console.log('Is User Logged in: '+ !(user === null))
    return !(user === null)
  }

  logOut() {
    sessionStorage.removeItem('username')
  }
}

We also have TodoService (todo.service.ts) to perform various operations on Todo information.

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable, of, throwError } from 'rxjs';
import { map, catchError, tap, retry } from 'rxjs/operators';
import { Todo } from './../domain/Todo';

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

  endpoint = 'http://localhost:8081/api';

  username = 'myjavablog';
  password = 'password';

  constructor(private httpClient: HttpClient) {
  }

  // Http Options
  httpOptions = {
    headers: new HttpHeaders({
      'Content-Type': 'application/json'
    })
  }
  
  public deleteTodo(todoId) {

    const headers = new HttpHeaders({ Authorization: 'Bearer ' + btoa(this.username + ':' + this.password) });
    return this.httpClient.delete<Todo>("http://localhost:8081/api/todo" + "/" + todoId, { headers });
  }

  public createTodo(todo) {

    const headers = new HttpHeaders({ Authorization: 'Bearer ' + btoa(this.username + ':' + this.password) });
    return this.httpClient.post<Todo>("http://localhost:8081/api/todo", todo, { headers });
  }

  // To Get List Of Todos
  getTodoList(): Observable<any> {
    
    const headers = new HttpHeaders({ Authorization: 'Bearer ' + btoa(this.username + ':' + this.password) });
    return this.httpClient.get<Todo[]>('http://localhost:8081/api/todos', { headers });
  }

  // HttpClient API get() method => Fetch Todo
  getTodo(id): Observable<Todo> {

    const headers = new HttpHeaders({ Authorization: 'Bearer ' + btoa(this.username + ':' + this.password) });
    return this.httpClient.get<Todo>(this.endpoint + '/todos/' + id, { headers });
  }
  
  // HttpClient API put() method => Update Todo
  updateTodo(id, todo): Observable<Todo> {
    
    const headers = new HttpHeaders({ Authorization: 'Bearer ' + btoa(this.username + ':' + this.password) });
    return this.httpClient.put<Todo>(this.endpoint + '/todo' , JSON.stringify(todo), { headers });
      
  }
}

You can download source code for this application from below link –

Step 6: Run the application

ng serve

Login page

Login page

Home page

Home Page
Generated Token during login

Add todo –

Add Todo

Leave a Comment

Bitnami