refresh token implementation
This commit is contained in:
Родитель
4a464dd483
Коммит
25864e5471
|
@ -1,11 +1,12 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { Observable, Subject, of } from 'rxjs';
|
||||
import { Injectable, Injector } from '@angular/core';
|
||||
import { Observable, Subject, of, BehaviorSubject } from 'rxjs';
|
||||
import { MessageService } from './message/message.service';
|
||||
import { LogService } from './log/log.service';
|
||||
import { TokenService } from './auth/token.service';
|
||||
import { UtilsService } from './utils/utils.service';
|
||||
|
||||
import { HttpClient, HttpInterceptor, HttpHandler, HttpRequest, HttpEvent, HttpResponse, HttpErrorResponse, HttpHeaders } from '@angular/common/http';
|
||||
import { switchMap, filter, take, catchError } from 'rxjs/operators';
|
||||
import { throwError } from 'rxjs/internal/observable/throwError';
|
||||
|
||||
export interface IValidationErrorInfo {
|
||||
|
||||
|
@ -43,6 +44,14 @@ export interface IAjaxResponse {
|
|||
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export abstract class RefreshTokenService {
|
||||
/**
|
||||
* Try to authenticate with refresh token and return if auth succeed
|
||||
*/
|
||||
abstract tryAuthWithRefreshToken(): Observable<boolean>;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class AbpHttpConfiguration {
|
||||
|
||||
|
@ -128,9 +137,9 @@ export class AbpHttpConfiguration {
|
|||
|
||||
handleAbpResponse(response: HttpResponse<any>, ajaxResponse: IAjaxResponse): HttpResponse<any> {
|
||||
var newResponse: HttpResponse<any>;
|
||||
|
||||
|
||||
if (ajaxResponse.success) {
|
||||
|
||||
|
||||
newResponse = response.clone({
|
||||
body: ajaxResponse.result
|
||||
});
|
||||
|
@ -160,7 +169,7 @@ export class AbpHttpConfiguration {
|
|||
}
|
||||
|
||||
getAbpAjaxResponseOrNull(response: HttpResponse<any>): IAjaxResponse | null {
|
||||
if(!response || !response.headers) {
|
||||
if (!response || !response.headers) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -174,7 +183,7 @@ export class AbpHttpConfiguration {
|
|||
this._logService.warn('Content-Type is not application/json: ' + contentType);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
var responseObj = JSON.parse(JSON.stringify(response.body));
|
||||
if (!responseObj.__abp) {
|
||||
return null;
|
||||
|
@ -198,12 +207,12 @@ export class AbpHttpConfiguration {
|
|||
observer.next("");
|
||||
observer.complete();
|
||||
} else {
|
||||
let reader = new FileReader();
|
||||
reader.onload = function() {
|
||||
let reader = new FileReader();
|
||||
reader.onload = function () {
|
||||
observer.next(this.result);
|
||||
observer.complete();
|
||||
}
|
||||
reader.readAsText(blob);
|
||||
reader.readAsText(blob);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -211,37 +220,91 @@ export class AbpHttpConfiguration {
|
|||
|
||||
@Injectable()
|
||||
export class AbpHttpInterceptor implements HttpInterceptor {
|
||||
|
||||
|
||||
protected configuration: AbpHttpConfiguration;
|
||||
private _tokenService: TokenService = new TokenService();
|
||||
private _utilsService: UtilsService = new UtilsService();
|
||||
private _logService: LogService = new LogService();
|
||||
|
||||
constructor(configuration: AbpHttpConfiguration) {
|
||||
constructor(configuration: AbpHttpConfiguration,
|
||||
private _injector: Injector) {
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
|
||||
var interceptObservable = new Subject<HttpEvent<any>>();
|
||||
var modifiedRequest = this.normalizeRequestHeaders(request);
|
||||
|
||||
next.handle(modifiedRequest)
|
||||
.subscribe((event: HttpEvent<any>) => {
|
||||
this.handleSuccessResponse(event, interceptObservable );
|
||||
}, (error: any) => {
|
||||
return this.handleErrorResponse(error, interceptObservable);
|
||||
});
|
||||
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
|
||||
return interceptObservable;
|
||||
}
|
||||
var interceptObservable = new Subject<HttpEvent<any>>();
|
||||
var modifiedRequest = this.normalizeRequestHeaders(request);
|
||||
next.handle(modifiedRequest)
|
||||
.pipe(
|
||||
catchError(error => {
|
||||
if (error instanceof HttpErrorResponse && error.status === 401) {
|
||||
return this.tryAuthWithRefreshToken(request, next, error);
|
||||
} else {
|
||||
return throwError(error);
|
||||
}
|
||||
})
|
||||
).subscribe(
|
||||
(event: HttpEvent<any>) => {
|
||||
this.handleSuccessResponse(event, interceptObservable);
|
||||
},
|
||||
(error: any) => {
|
||||
return this.handleErrorResponse(error, interceptObservable);
|
||||
}
|
||||
);
|
||||
|
||||
protected normalizeRequestHeaders(request: HttpRequest<any>):HttpRequest<any> {
|
||||
return interceptObservable;
|
||||
}
|
||||
|
||||
protected tryGetRefreshTokenService(): Observable<boolean> {
|
||||
try {
|
||||
var _refreshTokenService = this._injector.get(RefreshTokenService);
|
||||
|
||||
if (_refreshTokenService) {
|
||||
return _refreshTokenService.tryAuthWithRefreshToken();
|
||||
}
|
||||
} catch (error) {
|
||||
return of(false);
|
||||
}
|
||||
return of(false);
|
||||
}
|
||||
|
||||
private isRefreshing = false;
|
||||
private refreshTokenSubject: BehaviorSubject<any> = new BehaviorSubject<any>(null);
|
||||
|
||||
private tryAuthWithRefreshToken(request: HttpRequest<any>, next: HttpHandler, error: any) {
|
||||
if (!this.isRefreshing) {
|
||||
this.isRefreshing = true;
|
||||
this.refreshTokenSubject.next(null);
|
||||
|
||||
return this.tryGetRefreshTokenService().pipe(
|
||||
switchMap((authResult: boolean) => {
|
||||
this.isRefreshing = false;
|
||||
if (authResult) {
|
||||
this.refreshTokenSubject.next(authResult);
|
||||
let modifiedRequest = this.normalizeRequestHeaders(request);
|
||||
return next.handle(modifiedRequest);
|
||||
} else {
|
||||
return throwError(error);
|
||||
}
|
||||
}));
|
||||
} else {
|
||||
return this.refreshTokenSubject.pipe(
|
||||
filter(token => token != null),
|
||||
take(1),
|
||||
switchMap(authResult => {
|
||||
let modifiedRequest = this.normalizeRequestHeaders(request);
|
||||
return next.handle(modifiedRequest);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
protected normalizeRequestHeaders(request: HttpRequest<any>): HttpRequest<any> {
|
||||
var modifiedHeaders = new HttpHeaders();
|
||||
modifiedHeaders = request.headers.set("Pragma","no-cache")
|
||||
.set("Cache-Control","no-cache")
|
||||
.set("Expires", "Sat, 01 Jan 2000 00:00:00 GMT");
|
||||
|
||||
modifiedHeaders = request.headers.set("Pragma", "no-cache")
|
||||
.set("Cache-Control", "no-cache")
|
||||
.set("Expires", "Sat, 01 Jan 2000 00:00:00 GMT");
|
||||
|
||||
modifiedHeaders = this.addXRequestedWithHeader(modifiedHeaders);
|
||||
modifiedHeaders = this.addAuthorizationHeaders(modifiedHeaders);
|
||||
modifiedHeaders = this.addAspNetCoreCultureHeader(modifiedHeaders);
|
||||
|
@ -253,7 +316,7 @@ export class AbpHttpInterceptor implements HttpInterceptor {
|
|||
});
|
||||
}
|
||||
|
||||
protected addXRequestedWithHeader(headers:HttpHeaders):HttpHeaders {
|
||||
protected addXRequestedWithHeader(headers: HttpHeaders): HttpHeaders {
|
||||
if (headers) {
|
||||
headers = headers.set('X-Requested-With', 'XMLHttpRequest');
|
||||
}
|
||||
|
@ -261,7 +324,7 @@ export class AbpHttpInterceptor implements HttpInterceptor {
|
|||
return headers;
|
||||
}
|
||||
|
||||
protected addAspNetCoreCultureHeader(headers:HttpHeaders):HttpHeaders {
|
||||
protected addAspNetCoreCultureHeader(headers: HttpHeaders): HttpHeaders {
|
||||
let cookieLangValue = this._utilsService.getCookieValue("Abp.Localization.CultureName");
|
||||
if (cookieLangValue && headers && !headers.has('.AspNetCore.Culture')) {
|
||||
headers = headers.set('.AspNetCore.Culture', cookieLangValue);
|
||||
|
@ -270,7 +333,7 @@ export class AbpHttpInterceptor implements HttpInterceptor {
|
|||
return headers;
|
||||
}
|
||||
|
||||
protected addAcceptLanguageHeader(headers:HttpHeaders):HttpHeaders {
|
||||
protected addAcceptLanguageHeader(headers: HttpHeaders): HttpHeaders {
|
||||
let cookieLangValue = this._utilsService.getCookieValue("Abp.Localization.CultureName");
|
||||
if (cookieLangValue && headers && !headers.has('Accept-Language')) {
|
||||
headers = headers.set('Accept-Language', cookieLangValue);
|
||||
|
@ -279,7 +342,7 @@ export class AbpHttpInterceptor implements HttpInterceptor {
|
|||
return headers;
|
||||
}
|
||||
|
||||
protected addTenantIdHeader(headers:HttpHeaders):HttpHeaders {
|
||||
protected addTenantIdHeader(headers: HttpHeaders): HttpHeaders {
|
||||
let cookieTenantIdValue = this._utilsService.getCookieValue(abp.multiTenancy.tenantIdCookieName);
|
||||
if (cookieTenantIdValue && headers && !headers.has(abp.multiTenancy.tenantIdCookieName)) {
|
||||
headers = headers.set(abp.multiTenancy.tenantIdCookieName, cookieTenantIdValue);
|
||||
|
@ -288,8 +351,8 @@ export class AbpHttpInterceptor implements HttpInterceptor {
|
|||
return headers;
|
||||
}
|
||||
|
||||
protected addAuthorizationHeaders(headers:HttpHeaders): HttpHeaders {
|
||||
let authorizationHeaders = headers ? headers.getAll('Authorization'): null;
|
||||
protected addAuthorizationHeaders(headers: HttpHeaders): HttpHeaders {
|
||||
let authorizationHeaders = headers ? headers.getAll('Authorization') : null;
|
||||
if (!authorizationHeaders) {
|
||||
authorizationHeaders = [];
|
||||
}
|
||||
|
@ -304,22 +367,22 @@ export class AbpHttpInterceptor implements HttpInterceptor {
|
|||
return headers;
|
||||
}
|
||||
|
||||
protected handleSuccessResponse(event: HttpEvent<any>, interceptObservable: Subject<HttpEvent<any>>): void{
|
||||
protected handleSuccessResponse(event: HttpEvent<any>, interceptObservable: Subject<HttpEvent<any>>): void {
|
||||
var self = this;
|
||||
|
||||
if (event instanceof HttpResponse) {
|
||||
if (event.body instanceof Blob && event.body.type && event.body.type.indexOf("application/json") >= 0){
|
||||
if (event.body instanceof Blob && event.body.type && event.body.type.indexOf("application/json") >= 0) {
|
||||
var clonedResponse = event.clone();
|
||||
|
||||
|
||||
self.configuration.blobToText(event.body).subscribe(json => {
|
||||
const responseBody = json == "null" ? {}: JSON.parse(json);
|
||||
|
||||
const responseBody = json == "null" ? {} : JSON.parse(json);
|
||||
|
||||
var modifiedResponse = self.configuration.handleResponse(event.clone({
|
||||
body: responseBody
|
||||
}));
|
||||
|
||||
|
||||
interceptObservable.next(modifiedResponse.clone({
|
||||
body: new Blob([JSON.stringify(modifiedResponse.body)], {type : 'application/json'})
|
||||
body: new Blob([JSON.stringify(modifiedResponse.body)], { type: 'application/json' })
|
||||
}));
|
||||
|
||||
interceptObservable.complete();
|
||||
|
@ -336,14 +399,14 @@ export class AbpHttpInterceptor implements HttpInterceptor {
|
|||
protected handleErrorResponse(error: any, interceptObservable: Subject<HttpEvent<any>>): Observable<any> {
|
||||
var errorObservable = new Subject<any>();
|
||||
|
||||
if(!(error.error instanceof Blob)){
|
||||
if (!(error.error instanceof Blob)) {
|
||||
interceptObservable.error(error);
|
||||
interceptObservable.complete();
|
||||
return of({});
|
||||
}
|
||||
|
||||
this.configuration.blobToText(error.error).subscribe(json => {
|
||||
const errorBody = (json == "" || json == "null") ? {}: JSON.parse(json);
|
||||
const errorBody = (json == "" || json == "null") ? {} : JSON.parse(json);
|
||||
const errorResponse = new HttpResponse({
|
||||
headers: error.headers,
|
||||
status: error.status,
|
||||
|
@ -351,7 +414,7 @@ export class AbpHttpInterceptor implements HttpInterceptor {
|
|||
});
|
||||
|
||||
var ajaxResponse = this.configuration.getAbpAjaxResponseOrNull(errorResponse);
|
||||
|
||||
|
||||
if (ajaxResponse != null) {
|
||||
this.configuration.handleAbpResponse(errorResponse, ajaxResponse);
|
||||
} else {
|
||||
|
@ -359,11 +422,11 @@ export class AbpHttpInterceptor implements HttpInterceptor {
|
|||
}
|
||||
|
||||
errorObservable.complete();
|
||||
|
||||
|
||||
interceptObservable.error(error);
|
||||
interceptObservable.complete();
|
||||
});
|
||||
|
||||
|
||||
return errorObservable;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,4 +19,20 @@ export class TokenService {
|
|||
abp.auth.setToken(authToken, expireDate);
|
||||
}
|
||||
|
||||
//refresh token
|
||||
getRefreshToken(): string {
|
||||
return abp.auth.getRefreshToken();
|
||||
}
|
||||
|
||||
getRefreshTokenCookieName(): string {
|
||||
return abp.auth.refreshTokenCookieName;
|
||||
}
|
||||
|
||||
clearRefreshToken(): void {
|
||||
abp.auth.clearRefreshToken();
|
||||
}
|
||||
|
||||
setRefreshToken(refreshToken: string, expireDate?: Date): void {
|
||||
abp.auth.setRefreshToken(refreshToken, expireDate);
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче