2017-11-21 8 views
0

제목에서 알 수 있듯이 OAuth 인증을 사용하여 Angular 4 프로젝트를 진행하고 있습니다.각도 4 및 OAuth - 401 응답을 가로 채고 액세스 토큰을 새로 고침하고 요청을 다시 시도

http 요청이 상태 코드 401로 응답 할 때마다 요청을 가로 채고 액세스 토큰을 갱신하고 실패한 요청을 다시 시도합니다.

401이 수신되면 요청이 올바르게 가로 채고 액세스 토큰이 정상적으로 새로 고쳐집니다. 실패한 요청은 다시 실행되지만 더 이상 응답을 구성 요소에 전달하지 않습니다.

문제는 요청 응답에서 관찰해야하는 구성 요소가 토큰 새로 고침 및 요청 재 시도 전에 '속성 수신 오류'로그로 불만을 토로한다는 것입니다.

내 인터셉터 :

import { Injectable, Inject, Injector } from '@angular/core'; 
import { 
    HttpRequest, 
    HttpHandler, 
    HttpResponse, 
    HttpErrorResponse, 
    HttpEvent, 
    HttpInterceptor, 
    HttpSentEvent, 
    HttpHeaderResponse, 
    HttpProgressEvent, 
    HttpUserEvent 
} from '@angular/common/http'; 
import { Observable } from 'rxjs/Observable'; 
import { AuthService } from './auth.service'; 
import { BehaviorSubject } from 'rxjs/BehaviorSubject'; 
import { TokenManager } from '../../../util/TokenManager'; 
import { AuthUserResponse } from '../../../models/authUserResponse'; 
import 'rxjs/add/operator/switchMap'; 

@Injectable() 
export class AuthTokenExpiredInterceptor implements HttpInterceptor { 

    isRefreshingToken: boolean = false; 
    tokenSubject: BehaviorSubject<string> = new BehaviorSubject<string>(null); 

    constructor(private injector: Injector, private tokenManager: TokenManager) {} 


    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpSentEvent | HttpHeaderResponse | HttpProgressEvent | HttpResponse<any> | HttpUserEvent<any>> { 
    return next.handle(this.addNewAccessTokenToHeaders(request, this.tokenManager.retrieveAccessToken())) 
    .do((event: HttpEvent<any>) => { 
     if (event instanceof HttpResponse) { 
      console.log('processing response', event); 
     } 
     return event; 
    },(err) => { 
     if (err instanceof HttpErrorResponse) { 
     if (err.status === 401) { 
      console.log('Access_token possibly expired, trying to retrieve a new one!') 

      return this.handle401Error(request, next); 
     } else if (err.status === 400) { 
      console.log('Refresh_token possibly expired, redirecting to login'); 

      return this.handle400Error(err); 
     } 
     } else { 
     return Observable.throw(err); 
     } 
    }); 
    } 

    handle401Error(request: HttpRequest<any>, next: HttpHandler) { 
    if (!this.isRefreshingToken) { 
     console.log('in if'); 
     this.isRefreshingToken = true; 

     // Reset here so that the following requests wait until the token comes back from the refreshToken call. 
     this.tokenSubject.next(null); 

     console.log('About to call renewAccessToken'); 
     return this.injector.get(AuthService).renewAccessToken().subscribe((response) => { 
     let newToken = response.access_token; 

     if (newToken) { 
      console.log('Got the new access_token!'); 
      this.tokenSubject.next(newToken); 
      let requestToRetry = this.addNewAccessTokenToHeaders(request, newToken); 
      console.log('The retried request header: ' + requestToRetry.headers.get("Authorization")); 
      return next.handle(requestToRetry); 
     } else { // No token in response 
      this.injector.get(AuthService).logout(); 
     } 
     }, 
     (err) => { 
     this.injector.get(AuthService).logout(); 
     return Observable.throw(err) 
     }, 
    () => { 
     console.log('handle401Error done'); 
     this.isRefreshingToken = false; 
     })   
    } else { 
     console.log('In else'); 
     return this.tokenSubject 
     .filter(token => token != null) 
     .take(1) 
     .switchMap(token => { 
     return next.handle(this.addNewAccessTokenToHeaders(request, token)); 
     }); 
    } 
    } 


    handle400Error(error: HttpErrorResponse) { 
     if (error && error.status === 400 && error.error && error.error.error === 'invalid_grant') { 
     this.injector.get(AuthService).logout(); 
     } 

     return Observable.throw(error); 
     } 

    addNewAccessTokenToHeaders(req: HttpRequest<any>, token: string): HttpRequest<any> { 
     console.log('Adding the access_token to the Authorization header'); 
     return req.clone({ setHeaders: { 
     Authorization: 'Bearer ' + token 
     }}) 
    } 
    } 

내 서비스는 관찰 가능한

내 구성 요소를 반환

ngOnInit(){ 
    this.getProperties(); 
    } 

    getProperties() { 
    this.propertyService.getProperties().subscribe(
     result => { 
     this.properties = result; 
     console.log('Received response in Properties component: ' + JSON.stringify(result)); 
    }, error => { 
     console.log('Error receiving the properties for the view') 
    }, 
    () => { console.log('Received the properties, now they can be displayed in the view') }) 
    } 

답변

1

함수의 절편은 항상 반환해야 관찰 가능한 < HttpEvent < 어떤>>. 귀하의 코드는 약간 "bizarro"입니다. 내가보기에 가장 중요한 문제는 "do"를 사용하여 오류를 잡는 것입니다. "do"는 요청을 수정하지 않습니다.

나는 이런 식의 절편을

constructor(private inj: Injector) { } 

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { 
    //if the request has "Authorization" we return the request 
    if (req.headers.has('Authorization')) 
     return next.handle(req); 

    //I get here the AuthService 
    const auth = this.inj.get(AuthService); 

    //create the httpHeaders 
    const httpHeaders = new HttpHeaders() 
     .set('Content-Type', 'application/json; charset=utf-8') 
     .set('Authorization', '' + auth.SID) //<-- I use auth.SID 

    const authReq = req.clone({ headers: httpHeaders }); 

    return next.handle(authReq).catch((err: any) => { //<--if error use a catch 
     if (err instanceof HttpErrorResponse) { 
     if (err.status === 401) { 
      //auth.recoverSID return a Observable<{value:new SID}> 
      //use switchMap to really return next.handle(authReq) 
      return auth.recoverSID().switchMap((value: IResponse) => { 
      let httpHeaders = new HttpHeaders() 
       .set('Content-Type', 'application/json; charset=utf-8') 
       .set('Authorization', '' + value.SID) 

      const authReq = req.clone({ headers: httpHeaders }); 
      return next.handle(authReq); 
      }) 
     }; 
     } 
     //Other case throw an error 
     return Observable.throw(err); 
    }); 
    } 
(I 코드가 당신을 도울 수 있기를 바랍니다)