Adding loging logout logic
This commit is contained in:
@@ -4,6 +4,8 @@
|
|||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="row flex-nowrap">
|
<div class="row flex-nowrap">
|
||||||
<div class="col-auto col-md-3 col-xl-2 px-sm-2 px-0 bg-dark" style="max-width: 200px;">
|
<div class="col-auto col-md-3 col-xl-2 px-sm-2 px-0 bg-dark" style="max-width: 200px;">
|
||||||
|
<login></login>
|
||||||
|
<logout></logout>
|
||||||
<sidenav class="sticky-top"></sidenav>
|
<sidenav class="sticky-top"></sidenav>
|
||||||
</div>
|
</div>
|
||||||
<div class="col py-3">
|
<div class="col py-3">
|
||||||
@@ -13,3 +15,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import { NgModule } from '@angular/core';
|
|||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||||
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons';
|
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons';
|
||||||
|
import { ReactiveFormsModule } from "@angular/forms";
|
||||||
|
import { HttpClientModule, HTTP_INTERCEPTORS } from "@angular/common/http";
|
||||||
|
|
||||||
import { AppRoutingModule } from './app-routing.module';
|
import { AppRoutingModule } from './app-routing.module';
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
@@ -9,20 +11,31 @@ import { AppComponent } from './app.component';
|
|||||||
import { SidenavComponent } from "./layout/sidenav/sidenav.component";
|
import { SidenavComponent } from "./layout/sidenav/sidenav.component";
|
||||||
import { FlashmessagesComponent } from "./layout/flashmessages/flashmessages.component";
|
import { FlashmessagesComponent } from "./layout/flashmessages/flashmessages.component";
|
||||||
import { FlashmessagesService } from "./layout/flashmessages/flashmessages.service";
|
import { FlashmessagesService } from "./layout/flashmessages/flashmessages.service";
|
||||||
|
import { LoginComponent, LogoutComponent } from "./layout/auth/auth.component";
|
||||||
|
import { AuthService } from "./layout/auth/auth.service";
|
||||||
|
import { AuthInterceptor } from "./layout/auth/auth.interceptor"
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
AppComponent,
|
AppComponent,
|
||||||
SidenavComponent,
|
SidenavComponent,
|
||||||
FlashmessagesComponent
|
FlashmessagesComponent,
|
||||||
|
LoginComponent,
|
||||||
|
LogoutComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
AppRoutingModule,
|
AppRoutingModule,
|
||||||
NgbModule,
|
NgbModule,
|
||||||
NgxBootstrapIconsModule.pick(allIcons),
|
NgxBootstrapIconsModule.pick(allIcons),
|
||||||
|
ReactiveFormsModule,
|
||||||
|
HttpClientModule,
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
FlashmessagesService,
|
||||||
|
AuthService,
|
||||||
|
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }
|
||||||
],
|
],
|
||||||
providers: [FlashmessagesService],
|
|
||||||
bootstrap: [AppComponent]
|
bootstrap: [AppComponent]
|
||||||
})
|
})
|
||||||
export class AppModule { }
|
export class AppModule { }
|
||||||
|
|||||||
103
front/app/src/app/layout/auth/auth.component.ts
Normal file
103
front/app/src/app/layout/auth/auth.component.ts
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
import {Component, ElementRef, EventEmitter, Output, ViewChild} from "@angular/core";
|
||||||
|
import {FormBuilder, FormGroup, Validators} from "@angular/forms";
|
||||||
|
import {Router} from "@angular/router";
|
||||||
|
import {AuthService} from "./auth.service";
|
||||||
|
import {NgbModal} from "@ng-bootstrap/ng-bootstrap";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'login',
|
||||||
|
template: `
|
||||||
|
<button class="btn btn-lg btn-outline-primary" (click)="openLoginModal()">Login</button>
|
||||||
|
<ng-template #loginModal let-modal>
|
||||||
|
<div class="modal-header">
|
||||||
|
<h4 class="modal-title" id="modal-basic-title">Login</h4>
|
||||||
|
<button type="button" class="btn-close" aria-label="Close"
|
||||||
|
(click)="modal.dismiss('Cross click')"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form [formGroup]="form">
|
||||||
|
<fieldset>
|
||||||
|
<legend>Login</legend>
|
||||||
|
<div class="form-field">
|
||||||
|
<label>Username:</label>
|
||||||
|
<input name="username" formControlName="username">
|
||||||
|
</div>
|
||||||
|
<div class="form-field">
|
||||||
|
<label>Password:</label>
|
||||||
|
<input name="password" formControlName="password"
|
||||||
|
type="password">
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
<div class="form-buttons">
|
||||||
|
<button class="button button-primary"
|
||||||
|
(click)="this.login()">Login</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
export class LoginComponent {
|
||||||
|
@Output() loginSuccess = new EventEmitter<string>()
|
||||||
|
|
||||||
|
@ViewChild('loginModal')
|
||||||
|
loginModal!: ElementRef;
|
||||||
|
|
||||||
|
form: FormGroup;
|
||||||
|
|
||||||
|
constructor(private fb:FormBuilder,
|
||||||
|
private authService: AuthService,
|
||||||
|
private modalService: NgbModal
|
||||||
|
|
||||||
|
) {
|
||||||
|
this.form = this.fb.group({
|
||||||
|
username: ['',Validators.required],
|
||||||
|
password: ['',Validators.required]
|
||||||
|
});
|
||||||
|
|
||||||
|
this.authService.onAuthenticationRequired$.subscribe((required: boolean) => {
|
||||||
|
if (required) {
|
||||||
|
this.openLoginModal()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
openLoginModal() {
|
||||||
|
this.modalService.open(this.loginModal, { ariaLabelledBy: 'modal-basic-title' }).result.then(
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
login() {
|
||||||
|
const val = this.form.value;
|
||||||
|
|
||||||
|
if (val.username && val.password) {
|
||||||
|
this.authService.login(val.username, val.password)
|
||||||
|
.subscribe((answer: string) => {
|
||||||
|
this.modalService.dismissAll();
|
||||||
|
this.loginSuccess.emit(answer)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'logout',
|
||||||
|
template: `
|
||||||
|
<button class="btn btn-lg btn-outline-primary" (click)="logout()">Logout</button>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
export class LogoutComponent {
|
||||||
|
@Output() logoutSuccess = new EventEmitter()
|
||||||
|
|
||||||
|
constructor(private authService: AuthService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
logout() {
|
||||||
|
this.authService.logout()
|
||||||
|
.subscribe(() => {
|
||||||
|
this.logoutSuccess.emit()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
34
front/app/src/app/layout/auth/auth.interceptor.ts
Normal file
34
front/app/src/app/layout/auth/auth.interceptor.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
import { HttpErrorResponse, HttpHandler, HttpInterceptor, HttpRequest } from "@angular/common/http";
|
||||||
|
|
||||||
|
import { Observable, throwError } from 'rxjs';
|
||||||
|
import { catchError } from "rxjs/operators";
|
||||||
|
|
||||||
|
import { AuthService } from './auth.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AuthInterceptor implements HttpInterceptor {
|
||||||
|
|
||||||
|
constructor(private auth: AuthService) {}
|
||||||
|
|
||||||
|
intercept(req: HttpRequest<any>, next: HttpHandler) {
|
||||||
|
const authToken = this.auth.getAuthorizationToken();
|
||||||
|
let authReq: HttpRequest<any>;
|
||||||
|
if (authToken) {
|
||||||
|
authReq = req.clone({
|
||||||
|
headers: req.headers.set('Authorization', authToken)
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
authReq = req;
|
||||||
|
}
|
||||||
|
|
||||||
|
return next.handle(authReq).pipe(
|
||||||
|
catchError((error: HttpErrorResponse) => {
|
||||||
|
if (error.status && error.status == 401) {
|
||||||
|
this.auth.requestAuth();
|
||||||
|
}
|
||||||
|
return throwError(() => error);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
63
front/app/src/app/layout/auth/auth.service.ts
Normal file
63
front/app/src/app/layout/auth/auth.service.ts
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
import { HttpClient, HttpHeaders, HttpParams } from "@angular/common/http";
|
||||||
|
|
||||||
|
import { map } from "rxjs/operators";
|
||||||
|
import { Subject } from "rxjs";
|
||||||
|
|
||||||
|
import { FlashmessagesService } from "../flashmessages/flashmessages.service";
|
||||||
|
|
||||||
|
export interface token_answer {
|
||||||
|
access_token: string,
|
||||||
|
token_type: string
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AuthService {
|
||||||
|
private access_token: string | null;
|
||||||
|
|
||||||
|
private authenticationRequired = new Subject<boolean>();
|
||||||
|
onAuthenticationRequired$ = this.authenticationRequired.asObservable();
|
||||||
|
|
||||||
|
constructor(private http: HttpClient,
|
||||||
|
private flashMessage: FlashmessagesService) {
|
||||||
|
this.access_token = localStorage.getItem('authtoken')
|
||||||
|
}
|
||||||
|
|
||||||
|
login(username:string, password:string ) {
|
||||||
|
const body = new HttpParams()
|
||||||
|
.set('username', username)
|
||||||
|
.set('password', password);
|
||||||
|
|
||||||
|
return this.http.post<token_answer>('/api/v1/auth/login', body.toString(),
|
||||||
|
{
|
||||||
|
headers: new HttpHeaders()
|
||||||
|
.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||||
|
}).pipe(map( v => {
|
||||||
|
localStorage.setItem('authtoken', v.access_token);
|
||||||
|
this.access_token = v.access_token
|
||||||
|
this.flashMessage.success('Login successful. Welcome ' + username);
|
||||||
|
return username
|
||||||
|
} ));
|
||||||
|
}
|
||||||
|
|
||||||
|
logout() {
|
||||||
|
return this.http.post<token_answer>('/api/v1/auth/logout', '',
|
||||||
|
{
|
||||||
|
headers: new HttpHeaders()
|
||||||
|
}).pipe(map( v => {
|
||||||
|
localStorage.removeItem('authtoken');
|
||||||
|
this.access_token = null;
|
||||||
|
this.flashMessage.success('Logout successful. Goodbye');
|
||||||
|
} ));
|
||||||
|
}
|
||||||
|
|
||||||
|
getAuthorizationToken() {
|
||||||
|
return `Bearer ${this.access_token}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
requestAuth() {
|
||||||
|
localStorage.removeItem('authtoken');
|
||||||
|
this.access_token = null;
|
||||||
|
this.authenticationRequired.next(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user