Merge pull request #1318 from GNS3/enhancement-JWT/1228

I have modified the JWT Interceptor and added refresh token functiona…
This commit is contained in:
Jeremy Grossmann 2022-05-31 15:58:18 +07:00 committed by GitHub
commit b430ab9a1c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 205 additions and 136 deletions

View File

@ -22,7 +22,14 @@
<mat-error *ngIf="loginForm.get('password').hasError('required')">You must enter password</mat-error> <mat-error *ngIf="loginForm.get('password').hasError('required')">You must enter password</mat-error>
</mat-form-field> </mat-form-field>
</form> </form>
<mat-checkbox
[disabled]="!isRememberMe"
[checked]="isRememberMeCheked"
class="margin-left"
color="primary"
(change)="rememberMe($event)"
>Remember me</mat-checkbox
>
<div class="buttons-bar"> <div class="buttons-bar">
<button class="loginButton" mat-raised-button color="primary" (click)="login()">Login</button> <button class="loginButton" mat-raised-button color="primary" (click)="login()">Login</button>
</div> </div>

View File

@ -35,3 +35,7 @@ mat-form-field {
justify-content: center; justify-content: center;
margin-top: 20px; margin-top: 20px;
} }
.margin-left {
margin-left: 5px;
}

View File

@ -1,15 +1,15 @@
import { Component, ComponentFactoryResolver, OnInit, ViewEncapsulation } from '@angular/core'; import { Component, DoCheck, OnInit, ViewEncapsulation } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms'; import { FormControl, FormGroup, Validators } from '@angular/forms';
import { ActivatedRoute, ParamMap, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { AuthResponse } from '../../models/authResponse';
import { Server } from '../../models/server'; import { Server } from '../../models/server';
import { Version } from '../../models/version';
import { LoginService } from '../../services/login.service'; import { LoginService } from '../../services/login.service';
import { ServerDatabase } from '../../services/server.database'; import { ServerDatabase } from '../../services/server.database';
import { ServerService } from '../../services/server.service'; import { ServerService } from '../../services/server.service';
import { ToasterService } from '../../services/toaster.service';
import { AuthResponse } from '../../models/authResponse';
import { VersionService } from '../../services/version.service';
import { Version } from '../../models/version';
import { ThemeService } from '../../services/theme.service'; import { ThemeService } from '../../services/theme.service';
import { ToasterService } from '../../services/toaster.service';
import { VersionService } from '../../services/version.service';
@Component({ @Component({
selector: 'app-login', selector: 'app-login',
@ -17,16 +17,18 @@ import { ThemeService } from '../../services/theme.service';
styleUrls: ['./login.component.scss'], styleUrls: ['./login.component.scss'],
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
}) })
export class LoginComponent implements OnInit { export class LoginComponent implements OnInit, DoCheck {
private server: Server; private server: Server;
public version: string; public version: string;
public isLightThemeEnabled: boolean = false; public isLightThemeEnabled: boolean = false;
public loginError: boolean = false; public loginError: boolean = false;
public returnUrl: string = ""; public returnUrl: string = '';
public isRememberMe: boolean = false;
public isRememberMeCheked: boolean = false;
loginForm = new FormGroup({ loginForm = new FormGroup({
username: new FormControl('', [Validators.required]), username: new FormControl('', [Validators.required]),
password: new FormControl('', [Validators.required]) password: new FormControl('', [Validators.required]),
}); });
constructor( constructor(
@ -58,32 +60,64 @@ export class LoginComponent implements OnInit {
this.themeService.getActualTheme() === 'light' this.themeService.getActualTheme() === 'light'
? (this.isLightThemeEnabled = true) ? (this.isLightThemeEnabled = true)
: (this.isLightThemeEnabled = false); : (this.isLightThemeEnabled = false);
let getCurrentUser = JSON.parse(localStorage.getItem(`isRememberMe`)) ?? null;
if (getCurrentUser && getCurrentUser.isRememberMe) {
this.loginForm.get('username').setValue(getCurrentUser.username);
this.loginForm.get('password').setValue(getCurrentUser.password);
this.isRememberMeCheked = getCurrentUser.isRememberMe;
}
} }
public login() { public login() {
if (this.loginForm.get('username').invalid || this.loginForm.get('password').invalid) { if (this.loginForm.get('username').invalid || this.loginForm.get('password').invalid) {
this.toasterService.error("Please enter username and password") this.toasterService.error('Please enter username and password');
return; return;
} }
let username = this.loginForm.get('username').value; let username = this.loginForm.get('username').value;
let password = this.loginForm.get('password').value; let password = this.loginForm.get('password').value;
this.loginService.login(this.server, username, password).subscribe(async (response: AuthResponse) => { this.loginService.login(this.server, username, password).subscribe(
async (response: AuthResponse) => {
let server = this.server; let server = this.server;
server.authToken = response.access_token; server.authToken = response.access_token;
server.username = username; server.username = username;
server.password = password; server.password = password;
server.tokenExpired = false;
await this.serverService.update(server); await this.serverService.update(server);
if (this.returnUrl.length <= 1) { if (this.returnUrl.length <= 1) {
this.router.navigate(['/server', this.server.id, 'projects']) this.router.navigate(['/server', this.server.id, 'projects']);
} else { } else {
this.router.navigateByUrl(this.returnUrl); this.router.navigateByUrl(this.returnUrl);
} }
},
}, error => { (error) => {
this.loginError = true; this.loginError = true;
}); }
);
}
rememberMe(ev) {
if (ev.checked) {
let curren_user = {
username: this.loginForm.get('username').value,
password: this.loginForm.get('password').value,
isRememberMe: ev.checked,
};
this.isRememberMeCheked = ev.checked;
localStorage.setItem(`isRememberMe`, JSON.stringify(curren_user));
} else {
localStorage.removeItem(`isRememberMe`);
this.loginForm.reset();
this.isRememberMe = ev.checked;
}
}
ngDoCheck() {
if (this.loginForm.get('username').valid && this.loginForm.get('password').valid) {
this.isRememberMe = true;
}
} }
} }

View File

@ -6,34 +6,19 @@ import { ServerService } from '../services/server.service';
@Injectable() @Injectable()
export class LoginGuard implements CanActivate { export class LoginGuard implements CanActivate {
constructor( constructor(private serverService: ServerService, private loginService: LoginService, private router: Router) {}
private serverService: ServerService,
private loginService: LoginService,
private router: Router
) {}
async canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot) { async canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
const server_id = next.paramMap.get('server_id'); const server_id = next.paramMap.get('server_id');
this.loginService.server_id = server_id;
let server = await this.serverService.get(parseInt(server_id, 10)); let server = await this.serverService.get(parseInt(server_id, 10));
try { try {
await this.loginService.getLoggedUser(server).toPromise(); await this.loginService.getLoggedUser(server);
} catch (e) { } catch (e) {}
if (e.status === 401) {
server.tokenExpired = true;
await this.serverService.update(server)
try {
let response = await this.loginService.login(server, server.username, server.password).toPromise();
server.authToken = response.access_token;
server.tokenExpired = false;
await this.serverService.update(server)
} catch (e) {
throw e;
}
}
}
return this.serverService.get(parseInt(server_id, 10)).then((server: Server) => { return this.serverService.get(parseInt(server_id, 10)).then((server: Server) => {
if (server.authToken) return true; if (server.authToken && !server.tokenExpired) {
return true;
}
this.router.navigate(['/server', server.id, 'login'], { queryParams: { returnUrl: state.url } }); this.router.navigate(['/server', server.id, 'login'], { queryParams: { returnUrl: state.url } });
}); });
} }

View File

@ -1,10 +1,45 @@
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpEvent, HttpResponse, HttpRequest, HttpHandler } from '@angular/common/http'; import { LoginService } from '@services/login.service';
import { Observable } from 'rxjs'; import { ServerService } from '@services/server.service';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
@Injectable() @Injectable()
export class HttpRequestsInterceptor implements HttpInterceptor { export class HttpRequestsInterceptor implements HttpInterceptor {
constructor(private serverService: ServerService, private loginService: LoginService) {}
intercept(httpRequest: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { intercept(httpRequest: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(httpRequest); return next.handle(httpRequest).pipe(
catchError((err) => {
if (err.status === 401 || err.status === 403) {
this.call();
} else {
return throwError(err);
}
})
);
}
async call() {
let getCurrentUser = JSON.parse(localStorage.getItem(`isRememberMe`)) ?? null;
const server_id = this.loginService.server_id;
let server = await this.serverService.get(parseInt(server_id, 10));
server.tokenExpired = true;
await this.serverService.update(server);
try {
if (getCurrentUser && getCurrentUser.isRememberMe) {
let response = await this.loginService.getLoggedUserRefToken(server, getCurrentUser);
server.authToken = response.access_token;
server.tokenExpired = false;
await this.serverService.update(server);
await this.loginService.getLoggedUser(server);
this.reloadCurrentRoute();
}
} catch (e) {
throw e;
}
}
reloadCurrentRoute() {
location.reload();
} }
} }

View File

@ -7,6 +7,7 @@ import { AuthResponse } from '../models/authResponse';
@Injectable() @Injectable()
export class LoginService { export class LoginService {
server_id:string =''
constructor(private httpServer: HttpServer) {} constructor(private httpServer: HttpServer) {}
login(server: Server, username: string, password: string) { login(server: Server, username: string, password: string) {
@ -22,6 +23,9 @@ export class LoginService {
} }
getLoggedUser(server: Server) { getLoggedUser(server: Server) {
return this.httpServer.get(server, "/users/me"); return this.httpServer.get(server, "/users/me").toPromise()
}
async getLoggedUserRefToken(server: Server,current_user):Promise<any> {
return await this.httpServer.post<AuthResponse>(server, "/users/authenticate", {"username":current_user.username,"password":current_user.password}).toPromise()
} }
} }