angular 7.のエラー401を受け取ったときに、更新トークンリクエストを自動化しようとしています。
その間に私はangular 7でそれを行う方法の多くのドキュメントを見つけません、そして私はangularまたはrxjsの以前の知識を持っていませんちょっとおかしい
私はそれはほぼ完了していると思いますが、何らかの理由で2番目のnext.handle(newReq)はリクエストを送信しません(google chromeネットワークデバッガでは最初のリクエストのみが表示されます)
私はリフレッシュの応答を取得し、processLoginResponse(res)を正しく作成しています
ここに私のインターセプターが見えます
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
let newReq = req.clone();
return next.handle(req).pipe(
catchError(error => {
if (error.status == 401) {
this._authenticationService.refresh().subscribe(
res => {
this._authenticationService.processLoginResponse(res);
newReq.headers.set("Authorization", "Bearer " + this._authenticationService.authResponse.token)
return next.handle(newReq)
},
error => {
this._authenticationService.logOut();
});
}
throw error;
})
);
以下は、更新トークンを呼び出すためのコードであり、更新トークンの呼び出しを取得した後、失敗したAPIを呼び出します。
ソースコード内のコメントは、フローを理解するのに役立ちます。以下のシナリオではテスト済みで問題ありません
1)401が原因で単一のリクエストが失敗した場合は、更新トークンが呼び出され、更新されたトークンで失敗したAPIが呼び出されます。
2)401が原因で複数のリクエストが失敗した場合、更新トークンが呼び出され、更新されたトークンで失敗したAPIが呼び出されます。
3)トークンAPIを繰り返し呼び出すことはありません
まだこのコードが機能しない新しいシナリオを誰かが見つけた場合は、私に知らせてください。テストし、それに応じて更新します。
import { Injectable } from "@angular/core";
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpErrorResponse, HttpHeaders, HttpClient, HttpResponse } from "@angular/common/http";
import { Observable } from "rxjs/Observable";
import { throwError, BehaviorSubject } from 'rxjs';
import { catchError, switchMap, tap, filter, take, finalize } from 'rxjs/operators';
import { TOKENAPIURL } from 'src/environments/environment';
import { SessionService } from '../services/session.service';
import { AuthService } from '../services/auth.service';
/**
* @author Pravin P Patil
* @version 1.0
* @description Interceptor for handling requests which giving 401 unauthorized and will call for
* refresh token and if token received successfully it will call failed (401) api again for maintaining the application momentum
*/
@Injectable()
export class RefreshTokenInterceptor implements HttpInterceptor {
private isRefreshing = false;
private refreshTokenSubject: BehaviorSubject<any> = new BehaviorSubject<any>(null);
constructor(private http: HttpClient, private sessionService: SessionService, private authService: AuthService) { }
/**
*
* @param request HttpRequest
* @param next HttpHandler
* @description intercept method which calls every time before sending requst to server
*/
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// Taking an access token
const accessToken = sessionStorage.getItem('ACCESS_TOKEN');
// cloing a request and adding Authorization header with token
request = this.addToken(request, accessToken);
// sending request to server and checking for error with status 401 unauthorized
return next.handle(request).pipe(
catchError(error => {
if (error instanceof HttpErrorResponse && error.status === 401) {
// calling refresh token api and if got success extracting token from response and calling failed api due to 401
return this.handle401Error(request, next);
} // If api not throwing 401 but gives an error throwing error
else {
return throwError(error);
}
}));
}
/**
*
* @param request HttpRequest<any>
* @param token token to in Authorization header
*/
private addToken(request: HttpRequest<any>, token: string) {
return request.clone({
setHeaders: { 'Authorization': `Bearer ${token}` }
});
}
/**
* This method will called when any api fails due to 401 and calsl for refresh token
*/
private handle401Error(request: HttpRequest<any>, next: HttpHandler) {
// If Refresh token api is not already in progress
if (this.isRefreshing) {
// If refreshTokenInProgress is true, we will wait until refreshTokenSubject has a non-null value
// – which means the new token is ready and we can retry the request again
return this.refreshTokenSubject
.pipe(
filter(token => token != null),
take(1),
switchMap(jwt => {
return next.handle(this.addToken(request, jwt))
}));
} else {
// updating variable with api is in progress
this.isRefreshing = true;
// Set the refreshTokenSubject to null so that subsequent API calls will wait until the new token has been retrieved
this.refreshTokenSubject.next(null);
const refreshToken = sessionStorage.getItem('REFRESH_TOKEN');
// Token String for Refresh token OWIN Authentication
const tokenDataString = `refresh_token=${refreshToken}&grant_type=refresh_token`;
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/x-www-form-urlencoded',
'X-Skip-Interceptor': ''
})
};
return this.http.post<any>(TOKENAPIURL, tokenDataString, httpOptions)
.pipe(switchMap((tokens) => {
this.isRefreshing = false;
this.refreshTokenSubject.next(tokens.access_token);
// updating value of expires in variable
sessionStorage.setItem('ACCESS_TOKEN', tokens.access_token);
return next.handle(this.addToken(request, tokens.access_token));
}));
}
}
}