Introduction

Here we will create Spring Boot Security Angular Login Logout Example which will show you how to integrate Spring Security with Angular 8 to login to the application. We will redirect user to the login page if user is not already logged in. Similarly we will redirect user to the home page if user is already logged in. Because if user logged in then there is no point of showing the login page again when user click on the browser’s back button.

Spring Boot’s default security mechanism will ask for username and password when you try to access the REST GET API from browser. The default username is user and password is generated in the console of the Spring Boot application during start up.

We can also configure the password which is easy to remember in the application.properties or application.yml file. In this example we will configure both user name and password in the application.properties file.

In the frontend using Angular we will consume the REST API to authenticate the user and if the user is successfully authenticated then user will be able to consume other REST API, in other words we will show greeting message with random generated string on UI.

At the beginning when Angular application starts, the UI will render the login page. Once user successfully logged in then user will be redirected to the home page where he/she will see greeting message with a random string.

Prerequisites

Eclipse 4.12, Gradle 5.6, Maven 3.6.1, Java at least 8, Angular 8, Spring Boot 2.2.2, Spring Boot Security 2.2.2

Server Application

First we will create server application using Java and Spring Boot.

Create Project

You may create gradle or maven based project in Eclipse or any IDE or tool of your choice.

If you are creating gradle based project then you can use below build.gradle script. The name of the project is spring-boot-security.

buildscript {
	ext {
		springBootVersion = '2.2.2.RELEASE'
	}
	
    repositories {
        mavenCentral()
    }
    
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

plugins {
    id 'java-library'
    id 'org.springframework.boot' version '2.2.2.RELEASE'
}

sourceCompatibility = 12
targetCompatibility = 12

repositories {
    mavenCentral()
}

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-web:${springBootVersion}")
    implementation("org.springframework.boot:spring-boot-starter-security:${springBootVersion}")
}

If you are creating maven based project then you can use below pom.xml file:

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.roytuts</groupId>
	<artifactId>spring-boot-security</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.2.2.RELEASE</version>
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>
	</dependencies>

    <build>
        <plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.8.1</version>
				<configuration>
					<source>at least 8</source>
					<target>at least 8</target>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

Security Configuration

We will configure security using Spring Security mechanism as shown below:

package com.roytuts.spring.boot.security.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
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;

@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.csrf().disable().authorizeRequests().antMatchers(HttpMethod.OPTIONS, "/**").permitAll().anyRequest()
				.authenticated().and().httpBasic();
	}

}

We override the configure() method, we disable csrf token, we apply the basic authentication.

Configure User Credentials

We configure username and password in the src/main/resources/application.properties file. The username is user and password is user.

spring.security.user.name=user
spring.security.user.password=user

REST Endpoints

We expose two REST endpoints – one is for authenticating user and another is for fetching greeting message.

We allow URL http://localhost:4200 to avoid any cross origin issues.

package com.roytuts.spring.boot.security.controller;

import java.security.Principal;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

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

	@GetMapping("/auth")
	public Principal user(Principal user) {
		return user;
	}

	@GetMapping("/resource")
	public Map<String, Object> home() {
		Map<String, Object> model = new HashMap<String, Object>();
		model.put("id", UUID.randomUUID().toString());
		model.put("content", "Hello World");
		return model;
	}

}

Main Class

A class with main method and @SpringBootAnnotation is enough to deploy the Spring Boot application into embedded Tomcat server.

package com.roytuts.spring.boot.security;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringBootAngularSecurityApp {

	public static void main(String[] args) {
		SpringApplication.run(SpringBootAngularSecurityApp.class, args);
	}
}

That’s all about server application. After deploying the application by running the above main class you will be able to access the endpoint /resource by authenticating the user using user/user when asked for the credentials.

Now let’s move on to creating the client application.

Client Application

Make sure to create Angular project. The name of the project is spring-boot-angular-security.

Change Global Title

You can change the global title at page index.html under src directory.

<title>Spring Boot Angular Security</title>

Authentication Service

The authentication service which provides some common functionalities such as authenticating user, whether user is logged in, the logged in user name etc.

You can create service file using command ng g s auth from the command line tool while you are on the project’s root directory. The file name will be generated as auth.service.ts under src/app directory.

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

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

	SESSION_KEY = 'auth_user'

	username: String;
	password: String;

	constructor(private http: HttpClient) {}

	authenticate(username: String, password: String) {
		return this.http.get(`http://localhost:8080/auth`, { 
			headers: { authorization: this.createBasicAuthToken(username, password) }}).pipe(map((res) => {
				this.username = username;
				this.password = password;				
				this.registerInSession(username, password);
		}));
	}

	createBasicAuthToken(username: String, password: String) {
		return 'Basic ' + window.btoa(username + ":" + password)
	}
	
	registerInSession(username, password) {
		sessionStorage.setItem(this.SESSION_KEY, username)
	}

	logout() {
		sessionStorage.removeItem(this.SESSION_KEY);
		this.username = null;
		this.password = null;
	}

	isUserLoggedin() {
		let user = sessionStorage.getItem(this.SESSION_KEY)
		if (user === null) return false
		return true
	}

	getLoggedinUser() {
		let user = sessionStorage.getItem(this.SESSION_KEY)
		if (user === null) return ''
		return user
	}
}

HttpInterceptor

This acts as a filter for each request. It checks if authorization is present in the HTTP header and if present continue with the header else it will add authorization header in HTTP request.

The following code is written into file httpInterceptor.service.ts.

import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { AuthService } from './auth.service';

@Injectable()
export class HttpInterceptorService implements HttpInterceptor {

    constructor(private authService: AuthService) { }

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        if (this.authService.isUserLoggedin() && req.url.indexOf('basicauth') === -1) {
            const request = req.clone({
                headers: new HttpHeaders({
                    'Content-Type': 'application/json',
                    'Authorization': `Basic ${window.btoa(this.authService.username + ":" + this.authService.password)}`
                })
            });
            return next.handle(request);
        }
        
		return next.handle(req);
    }
}

Login Page

The login component is created using the command ng g c login from the command line tool while you are on the root directory of the project.

The required files will be created under login folder under src/app directory.

The following code is written into login.component.html file.

<div id="login-box">
	<h2>Login Here</h2>
	<div style="color : red" *ngIf='error'>{{error}}</div>
	<form name='loginForm' (submit)="doLogin()">
		<table>
			<tr>
				<td>User:</td>
				<td><input type='text' name='username' [(ngModel)]="username"></td>
			</tr>
			<tr>
				<td>Password:</td>
				<td><input type='password' name='password' [(ngModel)]="password"/></td>
			</tr>
			<tr>
				<td colspan='2'><input name="submit" type="submit"
					value="Login" /></td>
			</tr>
		</table>
	</form>
</div>

Style Login Form

We apply basic style to the login form to align it on center.

The following code is written into login.component.css file.

.error {
	padding: 15px;
	margin-bottom: 20px;
	border: 1px solid transparent;
	border-radius: 4px;
	color: #a94442;
	background-color: #f2dede;
	border-color: #ebccd1;
}
.msg {
	padding: 15px;
	margin-bottom: 20px;
	border: 1px solid transparent;
	border-radius: 4px;
	color: #31708f;
	background-color: #d9edf7;
	border-color: #bce8f1;
}
#login-box {
	width: 500px;
	padding: 20px;
	margin: 50px auto;
	background: #fff;
	-webkit-border-radius: 2px;
	-moz-border-radius: 2px;
	border: 1px solid #000;
}

Authenticate User

We need to authenticate user once user tries to login to the application by providing the credentials (user/user).

The following code is written into login.component.ts file.

If user is already logged in then we redirect user to the home page when user clicks on the back button of the browser.

We have basic validation if user credentials are not valid or if something went wrong on server side.

import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { AuthService } from '../auth.service';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {

	username: string = '';
	password : string = '';

	isLoggedin = false;

	error: string;

	constructor(private route: ActivatedRoute, private router: Router, private authService: AuthService) {}

	ngOnInit() {
		this.isLoggedin = this.authService.isUserLoggedin();

		if(this.isLoggedin) {
			this.router.navigateByUrl('home');
		}
	}

	doLogin() {
		if(this.username !== '' && this.username !== null && this.password !== '' && this.password !== null) {
			this.authService.authenticate(this.username, this.password).subscribe((result)=> {
				console.log(result);
				this.router.navigate(['/home']);
			}, () => {		  
				this.error = 'Either invalid credentials or something went wrong';
			});
		} else {
			this.error = 'Invalid Credentials';
		}
	}
}

Home Page

The home component is created using the command ng g c home from the command line tool in the same way we created login component above.

The below code is written into home.component.html file.

In the below source we have two div and rendered according to whether user is logged in or not.

<h1>Greeting</h1>
<div [hidden]="!isLoggedin">
	<p>Welcome back, {{ loggedinUser }}!</p>
	<p>The ID is {{greeting.id}}</p>
	<p>The content is {{greeting.content}}</p>
	
	<p><a style="text-decoration: underline; cursor: pointer;" (click)=doLogout()>Logout</a></p>
</div>
<div [hidden]="isLoggedin">
	<p>Login to see your greeting</p>
</div>

Greeting Message

We call the REST API if user is logged in to show the greeting message on the home page. If user is not logged in then we redirect to the login page when user clicks on the back button of the browser.

The below code is written into home.component.ts file.

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

import { AuthService } from '../auth.service';
import { ActivatedRoute, Router } from '@angular/router';

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

	isLoggedin = false;
	
	loggedinUser: string = '';

	greeting = {};

	constructor(private route: ActivatedRoute, private router: Router, private http: HttpClient, private authService: AuthService) {}

	ngOnInit() {
		this.isLoggedin = this.authService.isUserLoggedin();
		this.loggedinUser = this.authService.getLoggedinUser();

		if(!this.isLoggedin) {
			this.router.navigateByUrl('login');
		}

		this.http.get('http://localhost:8080/resource').subscribe(data => this.greeting = data);
	}

	doLogout() {
		this.authService.logout();
		this.router.navigateByUrl('login');
	}

}

App Component

The app component is created during new Angular project creation and the page is displayed with the content which is written in this page.

Here we don’t want to display anything but according to our routing configurations put into routing component which we will see later.

<router-outlet></router-outlet>

Routing Module

We put all components and modules in the app.module.ts file under src/app directory. This is the file where we need to declare all modules or components used in the whole application.

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { LoginComponent } from './login/login.component';

import { FormsModule } from '@angular/forms';
import { HttpInterceptorService } from './httpInterceptor.service';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { HomeComponent } from './home/home.component';

@NgModule({
  declarations: [
    AppComponent,
    LoginComponent,
    HomeComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule,
    FormsModule
  ],
  providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: HttpInterceptorService,
      multi: true
    }
  ],
  bootstrap: [AppComponent]
})

export class AppModule { }

Route Configurations

As we said earlier that we want to display the page on UI according to the routing configurations which are kept in the file app-routing.module.ts under src/app directory.

By default it will redirect to the home page and home page will be displayed if user is logged in otherwise login page will be displayed.

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { HomeComponent } from './home/home.component';
import { LoginComponent } from './login/login.component';

const routes: Routes = [
  {path: '', redirectTo: 'home', pathMatch: 'full'},
  {path: 'home', component: HomeComponent},
  {path: 'login', component: LoginComponent}
];

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

export class AppRoutingModule { }

Testing the Application

Now it’s time to test our hard written application code. Make sure your Spring Boot Security application is running on Tomcat server at port 8080.

Then run your Angular application on port 4200 by executing command ng serve --open from the command line while you are on the project’s root directory.

Hit URL http://locahost:4200 in the browser or this URL will be opened automatically on the default browser on your system. The below page is displayed:

spring boot security angular login logout example

Login to the application using credentials user/user and you will see below greeting message with random generated string.

You will also see logout link to logout from the application.

spring boot security angular login logout example

That’s all.

Source Code

Spring Boot Security Angular Security

Thanks for reading.

Tags:

1 thought on “Spring Boot Security Angular 8 Login Logout Example

  1. Hello, how will i do a custom authentication for the part of the login process? BTW your how-to document is the easiest so far to be understood. Thanks!

Leave a Reply

Your email address will not be published. Required fields are marked *