In this tutorial:
1. We will create a Spring boot project with a simple REST API
2. We will add spring security to our spring boot project to secure REST API
3. We will create an Angular App using the latest Angular version 12.
4. We will implement login and logout features in the Angular App.
5. We will have a demo
6. Conclusion
Learn full stack app development using Spring boot and Angular 9 at
Spring Boot + Angular 9 CRUD Example Tutorial
1. Creating a Simple REST API using Spring Boot
http://localhost:8080/greeting
{"id":1,"content":"Hello, World!"}
http://localhost:8080/greeting?name=User
{"id":1,"content":"Hello, User!"}
1.1. Create a Spring Boot Application
>> Create Spring Boot Project in Spring Tool Suite [STS]
1.2. Add maven dependencies
<?xml version="1.0" encoding="UTF-8"?>
<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>net.javaguides.springboot</groupId>
<artifactId>Springboot-helloworld-application</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Springboot-helloworld-application</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
1.3. Create Greeting Model
package net.javaguides.springboot.helloworldapp.bean;
public class Greeting {
private final long id;
private final String content;
public Greeting(long id, String content) {
this.id = id;
this.content = content;
}
public long getId() {
return id;
}
public String getContent() {
return content;
}
}
1.4. Create a resource controller - GreetingController.java
package net.javaguides.springboot.helloworldapp.controller;
import java.util.concurrent.atomic.AtomicLong;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import net.javaguides.springboot.helloworldapp.bean.Greeting;
@CrossOrigin(origins = "http://localhost:4200")
@RestController
@RequestMapping("/api/v1")
public class GreetingController {
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
@RequestMapping("/greeting")
public Greeting greeting(@RequestParam(value = "name", defaultValue = "World") String name) {
return new Greeting(counter.incrementAndGet(), String.format(template, name));
}
}
@CrossOrigin(origins = "http://localhost:4200")
@RestController
@RequestMapping("/api/v1")
public class GreetingController {}
1.5. Running the Application
package net.javaguides.springboot.helloworldapp;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
$ mvn spring-boot:run
Now our Spring boot application will start running in embedded Tomcat server on port 8080 and point the browser to http://localhost:8080/.1.6 Test REST API
Now that the service is up, visit http://localhost:8080/api/v1/greeting, where you see:Provide a name query string parameter with http://localhost:8080/greeting?name=User. Notice how the value of the content attribute changes from "Hello, World!" to "Hello User!":
2. Security implementation in Spring boot application
2.1 Add spring-boot-starter-security Dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<?xml version="1.0" encoding="UTF-8"?>
<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>net.javaguides.springboot</groupId>
<artifactId>Springboot-helloworld-application</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Springboot-helloworld-application</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</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.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2.2 Default Security Setup
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
c8be15de-4488-4490-9dc6-fab3f91435c6
Default username - user
Default password -
c8be15de-4488-4490-9dc6-fab3f91435c6
2.3 Configure standard user and password
spring.security.user.name=javaguides
spring.security.user.password=password
2.3 Configure WebSecurityConfigurerAdapter
package net.javaguides.springboot.helloworldapp.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()
// .formLogin().and()
.httpBasic();
}
}
2.4 Define AuthenticationBean and BasicAuthController
package net.javaguides.springboot.helloworldapp.bean;
public class AuthenticationBean {
private String message;
public AuthenticationBean(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public String toString() {
return String.format("HelloWorldBean [message=%s]", message);
}
}
package net.javaguides.springboot.helloworldapp.controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import net.javaguides.springboot.helloworldapp.bean.AuthenticationBean;
@CrossOrigin(origins="http://localhost:4200")
@RestController
@RequestMapping("/api/v1")
public class BasicAuthController {
@GetMapping(path = "/basicauth")
public AuthenticationBean helloWorldBean() {
return new AuthenticationBean("You are authenticated");
}
}
2.5 Testing above Security Implementation using Postman Rest Client
3. Creating Angular App
http://localhost:8080/api/v1/greeting
C:\Users\RAMESH>node -v
v12.18.2
C:\Users\RAMESH>npm -v
6.14.5
3.1 Install the latest version of Angular CLI
npm install -g @angular/cli
C:\angular>ng --version
3.2 Create Angular 9 Application using Angular CLI
ng new angular-frontend-login
3.3 Create Angular Service & Components
- ng g s hello-world
– ng g c hello-world
– ng g c menu
– ng generate class Message
3.4 Integrate Bootstrap with Angular
npm install bootstrap jquery --save
...
"styles": [
"src/styles.css",
"node_modules/bootstrap/dist/css/bootstrap.min.css"
],
"scripts": [
"node_modules/jquery/dist/jquery.min.js",
"node_modules/bootstrap/dist/js/bootstrap.min.js"
]
...
3.5 Add Bootstrap CDN Link
/* You can add global styles to this file, and also import other style files */
@import url(https://unpkg.com/bootstrap@4.1.0/dist/css/bootstrap.min.css)
3.6 hello-world.service.ts -> HelloWorldService
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Message } from './message';
@Injectable({
providedIn: 'root'
})
export class HelloWordService {
constructor(private http: HttpClient) { }
helloWorldService() {
return this.http.get<Message>('http://localhost:8080/api/v1/greeting');
}
}
3.7 Model -> Message.ts
export class Message {
id: number;
content: string;
constructor(id: number, content: string) {
this.id = id;
this.content = content;
}
}
3.8 hello-world.component.ts -> HelloWorldComponent
import { Component, OnInit } from '@angular/core';
import { Message } from '../message';
import { HelloWordService } from '../hello-word.service';
@Component({
selector: 'app-hello-world',
templateUrl: './hello-world.component.html',
styleUrls: ['./hello-world.component.css']
})
export class HelloWorldComponent implements OnInit {
message: string;
constructor(private helloWorldService: HelloWordService) { }
ngOnInit() {
console.log("HelloWorldComponent");
this.helloWorldService.helloWorldService().subscribe( (result) => {
this.message = result.content;
});
}
}
3.9 hello-world.component.html -> HelloWorldComponent
<app-menu></app-menu>
<h1>{{message}}</h1>
3.10 menu.component.ts -> MenuComponent
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-menu',
templateUrl: './menu.component.html',
styleUrls: ['./menu.component.css']
})
export class MenuComponent implements OnInit {
welcomeMessage = 'Test';
constructor() { }
ngOnInit() {
}
}
3.11 menu.component.html -> MenuComponent
<header>
<nav class="navbar navbar-expand-md navbar-dark bg-dark">
<div><a href="https://www.javaguides.net" class="navbar-brand">JavaGuides</a></div>
<ul class="navbar-nav">
<li><a class="nav-link" href="/hello-world">Hello World Tab</a></li>
</ul>
</nav>
</header>
3.12 app.module.ts -> AppModule
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HelloWorldService } from './service/hello-world.service';
import { MenuComponent } from './menu/menu.component';
import { HelloWorldComponent } from './hello-world/hello-world.component';
@NgModule({
declarations: [
AppComponent,
MenuComponent,
HelloWorldComponent,
],
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule,
],
providers: [
HelloWorldService,
],
bootstrap: [AppComponent]
})
export class AppModule { }
3.13 app-routing.module.ts -> AppRoutingModule
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { HelloWorldComponent } from './hello-world/hello-world.component';
const routes: Routes = [
{path: '', component: HelloWorldComponent},
{path: 'hello-world', component: HelloWorldComponent},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
3.14 app.component.ts -> AppComponent
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'angular-frontend';
}
3.15 app.component.html -> AppComponent
<router-outlet></router-outlet>
3.16 Running Angular 9 Application
ng serve
ng serve --port 4204
4. Adding Basic Login and Logout implementation in Angular Application
4.1 Login Authentication Implementation
ng g c login
ng g s /login/auth
4.2 LoginComponent
import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { AuthenticationService } from './auth.service';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
username: string;
password : string;
errorMessage = 'Invalid Credentials';
successMessage: string;
invalidLogin = false;
loginSuccess = false;
constructor(
private route: ActivatedRoute,
private router: Router,
private authenticationService: AuthenticationService) { }
ngOnInit() {
}
handleLogin() {
this.authenticationService.authenticationService(this.username, this.password).subscribe((result)=> {
this.invalidLogin = false;
this.loginSuccess = true;
this.successMessage = 'Login Successful.';
this.router.navigate(['/hello-world']);
}, () => {
this.invalidLogin = true;
this.loginSuccess = false;
});
}
}
4.3 Login HTML Template
<app-menu></app-menu>
<div class="container col-lg-6">
<h1 class="text-center">Login</h1>
<div class="card">
<div class="card-body">
<form class="form-group">
<div class="alert alert-warning" *ngIf='invalidLogin'>{{errorMessage}}</div>
<div class="alert alert-success" *ngIf='loginSuccess'>{{successMessage}}</div>
<div class="form-group">
<label for="email">User Name :</label>
<input type="text" class="form-control" id="username" [(ngModel)]="username" placeholder="Enter User Name"
name="username">
</div>
<div class="form-group">
<label for="pwd">Password:</label>
<input type="password" class="form-control" [(ngModel)]="password" id="password" placeholder="Enter password"
name="password">
</div>
<button (click)=handleLogin() class="btn btn-success">Login</button>
</form>
</div>
</div>
</div>
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { HelloWorldComponent } from './hello-world/hello-world.component';
import { LoginComponent } from './login/login.component';
const routes: Routes = [
{path: 'login', component: LoginComponent},
{path: '', component: LoginComponent},
{path: 'hello-world', component: HelloWorldComponent},
{path: 'logout', component: LoginComponent},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
4.4 AuthenticationService
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { map } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class AuthenticationService {
// BASE_PATH: 'http://localhost:8080'
USER_NAME_SESSION_ATTRIBUTE_NAME = 'authenticatedUser'
public username: any;
public password: any;
constructor(private http: HttpClient) {
}
authenticationService(username: String, password: String) {
return this.http.get(`http://localhost:8080/api/v1/basicauth`,
{ headers: { authorization: this.createBasicAuthToken(username, password) } }).pipe(map((res) => {
this.username = username;
this.password = password;
this.registerSuccessfulLogin(username, password);
}));
}
createBasicAuthToken(username: String, password: String) {
return 'Basic ' + window.btoa(username + ":" + password)
}
registerSuccessfulLogin(username: String, password: any) {
sessionStorage.setItem(this.USER_NAME_SESSION_ATTRIBUTE_NAME, password)
}
logout() {
sessionStorage.removeItem(this.USER_NAME_SESSION_ATTRIBUTE_NAME);
this.username = null;
this.password = null;
}
isUserLoggedIn() {
let user = sessionStorage.getItem(this.USER_NAME_SESSION_ATTRIBUTE_NAME)
if (user === null) return false
return true
}
getLoggedInUserName() {
let user = sessionStorage.getItem(this.USER_NAME_SESSION_ATTRIBUTE_NAME)
if (user === null) return ''
return user
}
}
4.5 Add and Configure HttpInterceptor
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { AuthenticationService } from './login/auth.service';
@Injectable()
export class HttpInterceptorService implements HttpInterceptor {
constructor(private authenticationService: AuthenticationService) { }
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (this.authenticationService.isUserLoggedIn() && req.url.indexOf('basicauth') === -1) {
const authReq = req.clone({
headers: new HttpHeaders({
'Content-Type': 'application/json',
'Authorization': `Basic ${window.btoa(this.authenticationService.username + ":" + this.authenticationService.password)}`
})
});
return next.handle(authReq);
} else {
return next.handle(req);
}
}
}
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HelloWorldComponent } from './hello-world/hello-world.component';
import { MenuComponent } from './menu/menu.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';
@NgModule({
declarations: [
AppComponent,
HelloWorldComponent,
MenuComponent,
LoginComponent,
],
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule,
FormsModule
],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: HttpInterceptorService,
multi: true
}
],
bootstrap: [AppComponent]
})
export class AppModule { }
4.6 Logout Implementation
Let's create a logout component with the following command:ng g c logout
4.7 Add Logout Button to Menu Template
<header>
<nav class="navbar navbar-expand-md navbar-dark bg-dark">
<div><a href="https://www.javaguides.net" class="navbar-brand">JavaGuides</a></div>
<ul class="navbar-nav navbar-collapse justify-content-end">
<!-- <li>
<a *ngIf="!isLoggedIn" class="nav-link" href="/login">Login</a>
</li> -->
<li>
<a *ngIf="isLoggedIn" class="nav-link" href="/logout" (click)=handleLogout()>Logout</a>
</li>
</ul>
</nav>
</header>
import { Component, OnInit } from '@angular/core';
import { AuthenticationService } from '../login/auth.service';
import { ActivatedRoute, Router } from '@angular/router';
@Component({
selector: 'app-menu',
templateUrl: './menu.component.html',
styleUrls: ['./menu.component.css']
})
export class MenuComponent implements OnInit {
isLoggedIn = false;
constructor(private route: ActivatedRoute,
private router: Router,
private authenticationService: AuthenticationService) { }
ngOnInit() {
this.isLoggedIn = this.authenticationService.isUserLoggedIn();
console.log('menu ->' + this.isLoggedIn);
}
handleLogout() {
this.authenticationService.logout();
}
}
4.9 Update Routing Module
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { HelloWorldComponent } from './hello-world/hello-world.component';
import { LoginComponent } from './login/login.component';
const routes: Routes = [
{path: 'login', component: LoginComponent},
{path: '', component: LoginComponent},
{path: 'hello-world', component: HelloWorldComponent},
{path: 'logout', component: LoginComponent},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
5. Demo
Username: javaguides
Password: password
6. Conclusion
In this tutorial:
- We created a Spring boot project
- We created a simple REST API
- We have added spring security to our spring boot project to secure REST API
- We created the Angular App
- We have implemented login and logout features in the Angular App.
- Finally, we had a demo
Comments
Post a Comment
Leave Comment