==== * Step 1 - Creating project ng new TodoProject * Step 1' - Creating project ng serve * Step 2 - Generating Welcome Component with ng generate ng generate component welcome * Step 3 - Generating and Setting up Login Component ng generate component login * Step 4 - Draw basic form to login in app-component.html in login.component.html User name: Password: in login.component.ts username = "ayoub"; password= ""; handleClick(){ console.log(this.username); } ========== * Step 5 - Using ngModel with 2 Way Data Binding in Login Page in login.component.html User name: Password: in app.module.ts import { FormsModule } from '@angular/forms'; imports: [ BrowserModule, AppRoutingModule, FormsModule ], ========== * Step 6 - Adding Hardcoded Authentication to Logic Component - ngIf directive in login.component.html: {{errorMessage}}
User name: Password:
in login.component.ts errorMessage= 'Invalid Credentials'; invalidLogin = false; handleClick(){ // console.log(this.username); if (this.username==="ayoub" && this.password==="dummy") { this.invalidLogin = false; } else{ this.invalidLogin = true; } } ========== Step 7 - Implementing Routes for Login, Welcome and Error Components app.component.html: ng generate component error error.component.html: {{errorMessage}} error.component.ts: errorMessage="An error occured! Contact support at *** - ***" app-routing.module.ts: fill inside routes: const routes: Routes = [ {path:'', component:LoginComponent}, {path:'welcome', component:WelcomeComponent}, {path:'login', component:LoginComponent}, {path:'**', component:ErrorComponent} ]; ========== * Step 8 - Implementing Routing from Login to Welcome Component login.component.ts: constructor(private router: Router) { } handleClick(){ // console.log(this.username); if (this.username==="ayoub" && this.password==="dummy") { this.invalidLogin = false; this.router.navigate(['welcome']); } else{ this.invalidLogin = true; } import { Router } from '@angular/router'; ========== * Step 9 - Adding Route Parameter for Welcome Component in app-routing.module.ts const routes: Routes = [ {path:'', component:LoginComponent}, {path:'login', component:LoginComponent}, {path:'welcome/:name', component:WelcomeComponent}, {path:'**', component:ErrorComponent} ]; welcome.component.ts name: ""; constructor(private route: ActivatedRoute) { } ngOnInit() { this.name = this.route.snapshot.params['name']; } login.component.ts: // this.router.navigate(['welcome']); this.router.navigate(['welcome', this.username]); welcome.component.html:
Welcome {{name}}, Welcome to our awesome App.
========== * Step 10 - Create List Todos Component with ng generate ng generate component listTodos const routes: Routes = [ {path:'', component:LoginComponent}, {path:'login', component:LoginComponent}, {path:'welcome/:name', component:WelcomeComponent}, {path:'todos', component:ListTodosComponent}, {path:'**', component:ErrorComponent} ]; in list-todos.component.ts todos = [ {id: 1, description: 'Learn to cook'}, {id: 2, description: 'Become an expert at angular'}, {id: 3, description: 'Visit family'} ] in list-todos.component.html:
My Todo's
id description
{{todo.id}} {{todo.description}}
========== * Step 11 - Create a Link to Todos in Welcome Component welcome.component.html:
Welcome {{name}}, Welcome to our awesome App.
You wan manage your todos here
========== * Step 12 - Best Practice - Create a Todo Class list-todos.component.ts: // put this new Todo class above or under class ListTodosComponent export class Todo{ constructor( public id: number, public description: string, public done: boolean, public targetDate: Date ) { } } todos = [ new Todo(1, 'Learn to cook', false, new Date()), new Todo(2, 'Become an expert at angular', false, new Date()), new Todo(3, 'Visit family', false, new Date()) ] list-todos.component.html: add date and transform it through pipes
My Todo's
Description Target Date Is Completed?
{{todo.description}} {{todo.targetDate | date | uppercase}} {{todo.done}}
========== * Step 13 - Adding Bootstrap Framework and Creating Components for Menu and Footer styles.css: @import url(https://unpkg.com/bootstrap@4.1.0/dist/css/bootstrap.min.css) ng g c menu ng g c footer // html of menu and footer menu.component.html: TOP MENU ELEMENTS

footer.component.html:

FOOTER app.component.html: ========== * Step 14 - Using Bootstrap to Create a Menu with Navigation Links menu.component.html:
========== * Step 15 - Styling Footer and Other Components with CSS and Bootstrap footer.component.html: footer.component.css: .footer { position: absolute; bottom: 0; width:100%; height: 40px; background-color: #222222; } app.component.html:
list-todos.component.html:

My Todo's

+ remove caption put the
inside
login.component.html:

Login!

{{errorMessage}}
User name: Password:
========== * Step 16 - Creating an Independent Authentication Service Component ng generate service hardcodedAuthentication hardcoded-authentication.service.ts: authenticate(username, password){ if (username==="ayoub" && password==="dummy"){ return true; } return false; } login.component.ts: constructor(private router: Router, private hardcodedAuthenticationService: HardcodedAuthenticationService) { } handleClick(){ if (this.hardcodedAuthenticationService.authenticate(this.username, this.password)) { this.invalidLogin = false; this.router.navigate(['welcome', this.username]); } else{ this.invalidLogin = true; } } ========== * Step 17 - Using Session Storage to Store User Authentication Token hardcoded-authentication.service.ts: authenticate(username, password){ if (username==="ayoub" && password==="dummy"){ sessionStorage.setItem('authenticaterUser', username); return true; } return false; } isUserLoggedIn() { let user = sessionStorage.getItem('authenticaterUser') return !(user === null) } ========== * Step 18 - Enabling Menu Links Based on User Authentication Token menu.component.ts: constructor(private hardcodedAuthenticationService : HardcodedAuthenticationService) { } menu.component.html: conditional display using *ngIf
========== * Step 19 - Implementing Logout to remove User Authentication Token ng generate component logout const routes: Routes = [ {path:'', component:LoginComponent}, {path:'login', component:LoginComponent}, {path:'welcome/:name', component:WelcomeComponent}, {path:'todos', component:ListTodosComponent}, {path:'logout', component:LogoutComponent}, {path:'**', component:ErrorComponent} ]; logout.component.html:

You are logged out

Thank You For Using Our Application.
hardcoded-authentication.service.ts: logout(){ sessionStorage.removeItem('authenticaterUser') } logout.component.ts constructor(private hardcodedAuthenticationService: HardcodedAuthenticationService) { } ngOnInit() { this.hardcodedAuthenticationService.logout(); } ========== * Step 20 - Securing Components using Route Guards - Part 1 // will ban access to urls like /todos directly (unless logged in) ng generate service service/routeGuard RouteGuardService implements CanActivate // import { CanActivate } from '@angular/router'; RouteGuardService.ts: constructor(private hardcodedAuthenticationService: HardcodedAuthenticationService) { } canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { if (this.hardcodedAuthenticationService.isUserLoggedIn()) { return true; } return false; } {path:'welcome/:name', component:WelcomeComponent, canActivate: [RouteGuardService]}, {path:'todos', component:ListTodosComponent, canActivate: [RouteGuardService]}, test access to /todos while not logged in => blank page ==================== // redirect /welcome and /todos to /login route-guard.service.ts: constructor(private hardcodedAuthenticationService: HardcodedAuthenticationService , private router: Router) { } ----------------------- canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { if (this.hardcodedAuthenticationService.isUserLoggedIn()) { return true; } this.router.navigate(['login']); -------------------------------- return false; } ========== * Step 21 - Connecting Angular Frontend with Todo List RESTful Service ng generate service service/data/todoData // don't forget to import HttpClientModule in App.module.ts to be able to use HttpClient // import { HttpClientModule } from '@angular/common/http'; import { HttpClient } from '@angular/common/http'; todo-data.service.ts: constructor(private http:HttpClient) { } retrieveAllTodos(username) { return this.http.get(`http://localhost:8080/users/${username}/todos`); //console.log("Execute Hello World Bean Service") } list-todos.component.ts: todos: Todo[]; // todos = [ // new Todo(1, 'Learn to cook', false, new Date()), // new Todo(2, 'Become an expert at angular', false, new Date()), // new Todo(3, 'Visit family', false, new Date()) // ] constructor(private todoService:TodoDataService) { } ngOnInit() { this.todoService.retrieveAllTodos('ayoub').subscribe( response => { console.log(response); this.todos = response; } ) } test retrieving data, we have following error in FE console: // Test the button click => Access to XMLHttpRequest at 'http://localhost:8080/hello-world-bean' from origin 'http://localhost:4200' has been blocked by CORS policy: add to BE Controller: @RestController @CrossOrigin(origins="http://localhost:4200") or @CrossOrigin public class TodoJpaController { ========== * Step 22 - Adding Delete Todo Feature to Angular Frontend list-todos.component.html: todo-data.service.ts: deleteTodo(username, id){ return this.http.delete(`http://localhost:8080/users/${username}/todos/${id}`); } list-todos.component.ts message: string deleteTodo(id) { console.log(`delete todo ${id}` ) this.todoService.deleteTodo('ayoub', id).subscribe ( response => { console.log(response); this.message = `Delete of Todo ${id} Successful!`; this.refreshTodos(); } ) } ngOnInit() { this.refreshTodos(); } refreshTodos(){ this.todoService.retrieveAllTodos('ayoub').subscribe( response => { console.log(response); this.todos = response; } ) } list-todos.component.html
{{message}}
REMARK: look well at message between `` and not "" to evaluate ${id} // this.message = `Delete of Todo ${id} Successful!`; ========= ********** ========== ========== * Step 23 - Creating Todo Component and Handle Routing list-todos.component.html: ng generate component todo app-routing.module.ts { path: 'todos/:id', component: TodoComponent, canActivate:[RouteGuardService] }, list-todos.component.ts constructor( private todoService:TodoDataService, private router : Router ) { } updateTodo(id) { console.log(`update ${id}`) this.router.navigate(['todos',id]) } ========== * Step 24 - Designing Todo Page with Bootstrap Framework todo.component.html:

Todo

========== * Step 25 - Creating Retrieve Todo Service and Connect Angular Frontend getTodo() in java todo-data.service.ts: retrieveTodo(username, id){ return this.http.get(`http://localhost:8080/users/${username}/todos/${id}`); } todo.component.ts: id:number todo: Todo constructor( private todoService: TodoDataService, private route: ActivatedRoute ) { } ngOnInit() { this.id = this.route.snapshot.params['id']; this.todoService.retrieveTodo('ayoub', this.id) .subscribe ( data => this.todo = data ) } see now update page: => partially working, with 2 errors ========== * Step 26 - Improve Todo Page Appearance todo.component.ts: // first exception is because todo is null at first load // This is first exception: ERROR TypeError: Cannot read property 'description' of undefined ngOnInit() { this.id = this.route.snapshot.params['id']; this.todo = new Todo(1,'',false,new Date()); // <-- this.todoService.retrieveTodo('ayoub', this.id) .subscribe ( data => this.todo = data ) } // ERROR 2: date is not being filled: with this msg: // The specified value "2019-10-26T00:00:00.000+0000" does not conform to the required format, "yyyy-MM-dd". todo.component.html: ========== * Step 27 - Implementing Update Todo Feature in Angular Frontend todo-data.service.ts: updateTodo(username, id, todo){ return this.http.put( `http://localhost:8080/users/${username}/todos/${id}` , todo); } todo.component.ts: constructor( private todoService: TodoDataService, private route: ActivatedRoute, private router: Router ) { } saveTodo() { this.todoService.updateTodo('ayoub', this.id, this.todo) .subscribe ( data => { console.log(data) this.router.navigate(['todos']) } ) } ========== * Step 28 - Implementing New Todo Feature in Angular Frontend list-todos.component.html:
list-todos.component.ts: addTodo() { this.router.navigate(['todos',-1]) } todo.component.ts: ngOnInit() { this.id = this.route.snapshot.params['id']; // this.todo = new Todo(1,'',false,new Date()); this.todo = new Todo(this.id,'',false,new Date()); if(this.id!=-1) { this.todoService.retrieveTodo('ayoub', this.id) .subscribe ( data => this.todo = data ) } } // update this method saveTodo() { if(this.id == -1) { this.todoService.createTodo('ayoub', this.todo) .subscribe ( data => { console.log(data) this.router.navigate(['todos']) } ) } else { this.todoService.updateTodo('ayoub', this.id, this.todo) .subscribe ( data => { console.log(data) this.router.navigate(['todos']) } ) } } todo-data.service.ts createTodo(username, todo){ return this.http.post( `http://localhost:8080/users/${username}/todos` , todo); } ========== * Step 29 - Improving Todo Form - Validation and Form Submit on Enter - ngSubmit // 1- submit once clicked on enter // 2- don't allow empty filed + length < 5 todo.component.html: 1- put the 2
and the 1 2- // test that you cannot now insert a description smaller then 5 characters ========== * Step 30 - Enhancing Validation Messages on Todo Page todo.component.css: .ng-invalid:not(form) { border-left: 5px solid red; } todo.component.html: inside
put the 3 following lines:
Enter valid values
Enter valid Target Date
Enter atleast 5 characters in Description
========== Step 31 - Setting up Spring Security add to pom.xml org.springframework.boot spring-boot-starter-security restart the server, see in logs something similar to: Using generated security password: bc9891ac-19d1-4461-851d-94c2b65cbf7f open http://localhost:8080 you will be redirected to: http://localhost:8080/login user / bc9891ac-19d1-4461-851d-94c2b65cbf7f then you can now request your api TEST WITH POSTMAN ========== * Step 32 - Configure standard userid and password in application.properties, add: spring.security.user.name=ayoub spring.security.user.password=dummy ========== * Step 33 - Creating Angular HttpInterceptor to add Basic Auth Header ng generate service service/http/HttpIntercepterBasicAuth open it make it: implements HttpInterceptor implement intercept: put the code of createBasicAuthenticationHttpHeader() inside intercept(): => intercept(req: HttpRequest, next: HttpHandler){ let username = 'ayoub' let password = 'dummy' let basicAuthHeaderString = 'Basic ' + window.btoa(username + ':' + password); req = req.clone({ setHeaders: { Authorization: basicAuthHeaderString } }); return next.handle(req); } ========== * Step 34 - Configure HttpInterceptor as Provider in App Module app.module.ts: providers: [ {provide: HTTP_INTERCEPTORS, useClass: HttpIntercepterBasicAuthService, multi: true} ], test access to any todo project component => it works ========== * Step 35 - Create Basic Authentication RESTful Service in Spring Boot under com.hc.jee.webservices.day27restfulwebservices.basic.auth copy content of com.hc.jee.webservices.day27restfulwebservices.hello and rename it to - AuthenticationBean.java - BasicAuthenticationController.java keep only this in BasicAuthenticationController.java @GetMapping(path = "/basicauth") public AuthenticationBean helloWorldBean() { return new AuthenticationBean("You are authenticated"); } ========== * Step 36 - Create Angular Basic Authentication Service no more want to hard code the authn duplicate hardcoded-authentication.service.ts rename new one to basic-authentication.service.ts rename class name inside to BasicAuthenticationService add this method executeAuthenticationService(username, password) { let basicAuthHeaderString = 'Basic ' + window.btoa(username + ':' + password); let headers = new HttpHeaders({ Authorization: basicAuthHeaderString }) return this.http.get("http://localhost:8080/basicauth", {headers}); } Solve pbs by: - imporring needed classes - constructor(private http: HttpClient) { } - create AuthenticationBean below in the same file export class AuthenticationBean{ constructor(private message: string){} } ========== * Step 37 - Connect Login Page to Basic Authentication Service - Part 1 - go to login.component.ts: - create a // method to handleClick() (copy it) - call it handleBasicAuthLogin() constructor(private router: Router, private hardcodedAuthenticationService: HardcodedAuthenticationService, private basicAuthenticationService: BasicAuthenticationService) { } then use it as following here: login.component.ts: ================== handleBasicAuthLogin(){ this.basicAuthenticationService.executeAuthenticationService(this.username, this.password) .subscribe( data => { console.log(data) this.router.navigate(['welcome', this.username]) this.invalidLogin = false }, error => { console.log(error) this.invalidLogin = true } ) } basic-authentication.service.ts: to store the session in the sessionStorage ================================= import {map} from 'rxjs/operators'; executeAuthenticationService(username, password) { let basicAuthHeaderString = 'Basic ' + window.btoa(username + ':' + password); let headers = new HttpHeaders({ Authorization: basicAuthHeaderString }) return this.http.get( `http://localhost:8080/basicauth`, {headers}).pipe( map( data => { sessionStorage.setItem('authenticaterUser', username); return data; } ) ); } ========== * Step 38 - Connect Login Page to Basic Authentication Service - Part 2 login.component.html: Try login with dummy password (success) Try login with a wrong password: success also. This is because we are intercepting requests and addign basic auth header We will temporarily comment the interceptor: app.module.ts providers: [ // {provide: HTTP_INTERCEPTORS, useClass: HttpIntercepterBasicAuthService, multi: true} ], Try login with dummy password (success) Try login with a wrong password: FAIL THIS TIME :) . => OK ========== ========== * Step 39 - Refactoring Angular Basic Authentication Service basic-authentication.service.ts: sessionStorage.setItem('authenticaterUser', username); sessionStorage.setItem('token', basicAuthHeaderString); add those 2 methods: getAutheticatedUser() { return sessionStorage.getItem('authenticaterUser') } getAutheticatedToken() { if (this.getAutheticatedUser()) { return sessionStorage.getItem('token') } } logout(){ sessionStorage.removeItem('authenticaterUser') sessionStorage.removeItem('token') } ========== * Step 40 - Refactoring HttpInterceptor to use Basic Authentication Token Now though login can be successfully done, we will not be able to see todos (not authorized). http-intercepter-basic-auth.service.ts: constructor(private basicAuthenticationService: BasicAuthenticationService) { } intercept(req: HttpRequest, next: HttpHandler){ // let username = 'ayoub' // let password = 'dummy' // let basicAuthHeaderString = 'Basic ' + window.btoa(username + ':' + password); let basicAuthHeaderString = this.basicAuthenticationService.getAutheticatedToken(); let username = this.basicAuthenticationService.getAutheticatedUser(); if (basicAuthHeaderString && username) { req = req.clone({ setHeaders: { Authorization: basicAuthHeaderString } }); } return next.handle(req); } app.module.ts: providers: [ {provide: HTTP_INTERCEPTORS, useClass: HttpIntercepterBasicAuthService, multi: true} ], try todo menu: OK try login: OK ========== * Step 41 - Logout inject in constructor: private basicAuthenticationService: BasicAuthenticationService ngOnInit() { // this.hardcodedAuthenticationService.logout(); this.basicAuthenticationService.logout(); } ========== * Step 42 - Best Practice - Use Constants for URLs and Tokens create app.constants.ts under app folder put inside: export const API_URL = "http://localhost:8080" todo-data.service.ts: replace by http://localhost:8080 ${API_URL} test it basic-authentication.service.ts: export const TOKEN = 'token'; export const AUTHTICATED_USER = 'authenticaterUser'; replace them in all the file ******************************************************************************************************** ****************Connecting Spring Security and Spring Boot with JWT Framework ************************************ ******************************************************************************************************** ========== * Step 43 - Introduction to JWT ========== * Step 44 - Importing JWT Framework into Eclipse rename package com.hc.jee.webservices.day27restfulwebservices.basic.auth to com.hc.jee.rest.basic.auth to exclude the basic authn from our scope add io.jsonwebtoken jjwt 0.9.1 application.properties jwt.signing.key.secret=mySecret jwt.get.token.uri=/authenticate jwt.refresh.token.uri=/refresh jwt.http.request.header=Authorization jwt.token.expiration.in.seconds=604800 create com.hc.jee.webservices.day27restfulwebservices.jwt copy the jwt classes ========== * Step request via POSTMA: http://localhost:8080/users/ayoub/todos => 401 You would need to provide the Jwt Token to Access This resource How to get a JWT token: http://localhost:8080/authenticate in body: { "username": "ayoub", "password": "dummy" } ========== in basic-authentication.service.ts: executeJWTAuthenticationService(username, password) { return this.http.post( `${API_URL}/authenticate`,{ username, password }).pipe( map( data => { sessionStorage.setItem(AUTHTICATED_USER, username); sessionStorage.setItem(TOKEN, `Bearer ${data.token}`); return data; } ) ); //console.log("Execute Hello World Bean Service") } in basic-authentication.service.ts: create method: handleJWTAuthLogin() { this.basicAuthenticationService.executeJWTAuthenticationService(this.username, this.password) .subscribe( data => { console.log(data) this.router.navigate(['welcome', this.username]) this.invalidLogin = false }, error => { console.log(error) this.invalidLogin = true } ) } in login.component.html: replace old call to login by
Actions Update Delete