From f31d14d1cb32e8c8eabb05eb33eace6f259fdd5a Mon Sep 17 00:00:00 2001 From: Patrick Sun Date: Sat, 8 May 2021 05:00:33 +1000 Subject: [PATCH] auto logout by localstorage monitoring and remote websocket notifications --- src/app/app.component.ts | 16 +++++--- src/app/auth/auth.component.ts | 12 +++++- src/app/service/auth.service.ts | 7 +--- src/app/service/session.service.ts | 61 +++++++++++++++++++++++++----- 4 files changed, 74 insertions(+), 22 deletions(-) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index fdfebaf..a3f4221 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -61,11 +61,17 @@ export class AppComponent implements OnInit , OnDestroy { } onWSLogin(e: WsLoginEventModel): void { - if ( e.Mid === this.ss.loggedIn.machineId && e.Sid === this.ss.loggedIn.session ){ - if ( e.Uid !== this.ss.loggedIn.User.Id && e.Uid !== '') { - this.ss.logout(); - } + if ( e.T === 'logout' ) { // regardless where are they, logout means logout + if ( this.ss.isCurrentUser(e.Uid) ) { + this.ss.logoutAndClearLocalStorage(); + } + }else{ // some one logged in from another browser tab + if ( e.Mid === this.ss.loggedIn.machineId) { // same browser + if ( !this.ss.isCurrentUser(e.Uid) ) { + this.ss.logoutWithoutPersistingStorage(); + } + } } } - } + diff --git a/src/app/auth/auth.component.ts b/src/app/auth/auth.component.ts index 535e894..2808655 100644 --- a/src/app/auth/auth.component.ts +++ b/src/app/auth/auth.component.ts @@ -25,7 +25,11 @@ export class AuthComponent implements OnInit, OnDestroy{ private router: Router, private notificationService: NotificationService) { } ngOnInit(): void { - this.authService.logout(); + if ( this.ss.loggedIn.login ){ + this.ss.logout(); + } + + // this.ss.logoutAndClearLocalStorage(); this.loginSub = this.ss.loginSuccess.subscribe( responseData => { // console.log(responseData); @@ -52,6 +56,12 @@ export class AuthComponent implements OnInit, OnDestroy{ case 'admin': this.router.navigate(['/dashboard']); break; + case 'manager': + this.router.navigate(['/dashboard']); + break; + case 'accountant': + this.router.navigate(['/dashboard']); + break; case 'broker': this.router.navigate(['/broker-loan-list']); break; diff --git a/src/app/service/auth.service.ts b/src/app/service/auth.service.ts index 006a998..22f1562 100644 --- a/src/app/service/auth.service.ts +++ b/src/app/service/auth.service.ts @@ -46,11 +46,6 @@ export class AuthService { return true; } - saveSessionInfo(): void { - localStorage.setItem(this.config.storageKey, JSON.stringify(this.ss.loggedIn)); - localStorage.setItem('mid', this.ss.loggedIn.machineId); - } - login(email: string, password: string): void { this.http.post(this.config.apiUrl + 'login', {u: email, p: password}).subscribe( @@ -72,7 +67,7 @@ export class AuthService { this.ss.loggedIn.User = PeopleModel.EmptyNew(); } - this.saveSessionInfo(); + this.ss.saveSessionInfo(); this.ss.loginSuccess.emit(responseData); }, error => { diff --git a/src/app/service/session.service.ts b/src/app/service/session.service.ts index fafdfdb..dec0fd0 100644 --- a/src/app/service/session.service.ts +++ b/src/app/service/session.service.ts @@ -5,14 +5,34 @@ import {PeopleModel} from '../models/people.model'; import {HttpClient} from '@angular/common/http'; import {Router} from '@angular/router'; import {NotificationService} from '@progress/kendo-angular-notification'; +import {debounce} from 'ts-debounce'; @Injectable() export class SessionService { public loggedIn = ApiV1LoginResponse.EmptyNew(); loginSuccess = new EventEmitter (); + debouncedLocalStorageMonitor = debounce( this.localStorageChange, 1000); // to avoid to frequent local storage changes - constructor(private config: AppConfig, private http: HttpClient, private router: Router, private ns: NotificationService){ } + constructor(private config: AppConfig, private http: HttpClient, private router: Router, private ns: NotificationService){ + const self = this; + window.addEventListener('storage', event => { + if (event.storageArea === localStorage && event.key === this.config.storageKey) { + // It's local storage + self.debouncedLocalStorageMonitor(event).then( r => {}); + } + }, false); + } + + private localStorageChange(event: StorageEvent): void { + console.log(event); + const sfm: ApiV1LoginResponse = JSON.parse(localStorage.getItem(this.config.storageKey)); + if ( sfm && sfm.session && sfm.User && sfm.User.Id && this.loggedIn.User.Id) { + if ( sfm.session === this.loggedIn.session && sfm.User.Id !== this.loggedIn.User.Id){ + this.logoutWithoutPersistingStorage(); // silently logout without touching any storage + } + } + } public saveSessionInfo(): void { localStorage.setItem(this.config.storageKey, JSON.stringify(this.loggedIn)); @@ -32,7 +52,7 @@ export class SessionService { } public isCurrentUser(id: string): boolean { - return this.loggedIn.login === true && this.loggedIn.User !== undefined && this.loggedIn.User.Id === id; + return id !== '' && this.loggedIn.login === true && this.loggedIn.User !== undefined && this.loggedIn.User.Id === id; } public UpdatePeopleInfo(people: PeopleModel): void{ @@ -47,24 +67,45 @@ export class SessionService { return; } this.loggedIn = ApiV1LoginResponse.EmptyNew(); - localStorage.removeItem(this.config.storageKey); this.loginSuccess.emit(this.loggedIn); - this.router.navigate(['/login']).then(r => { - this.show(); - } ); + this.router.navigate(['/login']); + localStorage.removeItem(this.config.storageKey); this.http.post(`${this.config.apiUrl}logout`, '').subscribe( resp => { - console.log('successfully logout from server'); + this.show(); }, event => { - console.log('error logout from server', event); + this.show('logout from server (with warnings)'); } ); } - public show(): void { + logoutWithoutPersistingStorage(): void { + if ( !this.loggedIn.login ){ + return; + } + this.loggedIn = ApiV1LoginResponse.EmptyNew(); + this.loginSuccess.emit(this.loggedIn); + this.router.navigate(['/login']).then(r => { + this.show(); + } ); + } + + logoutAndClearLocalStorage(): void { + if ( !this.loggedIn.login ){ + return; + } + localStorage.removeItem(this.config.storageKey); + this.loggedIn = ApiV1LoginResponse.EmptyNew(); + this.loginSuccess.emit(this.loggedIn); + this.router.navigate(['/login']).then(r => { + this.show(); + } ); + } + + public show( msg: string = 'Successfully logged out'): void { this.ns.show({ - content: 'Successfully logged out', + content: msg, cssClass: 'button-notification', animation: { type: 'slide', duration: 400 }, position: { horizontal: 'center', vertical: 'top' },