使用Angular Interceptor的JWT过期令牌处理保持刷新

时间:2017-12-20 08:03:30

标签: javascript angular jwt

我在Angular中有一个拦截器,我用它来刷新令牌,如果它已经过期,但是当成功刷新令牌时,应用程序似乎陷入了对API的401错误的无休止调用。当我单步执行代码时,令牌确实会在过期时刷新,但之后会尝试重复刷新。

我还应该注意,再次单击该按钮并再次调用API时,应用程序会选择新令牌并在之后正常工作。如果没有这么多控制台错误,我很乐意让这个工作正常工作。

这是拦截器(旧)

import { Injectable, Injector } from "@angular/core";
import { Router } from "@angular/router";
import {
    HttpClient,
    HttpHandler, HttpEvent, HttpInterceptor,
    HttpRequest, HttpResponse, HttpErrorResponse
} from "@angular/common/http";
import { AuthService } from "./auth.service";
import { Observable } from "rxjs/Observable";

@Injectable()
export class AuthResponseInterceptor implements HttpInterceptor {

    currentRequest: HttpRequest<any>;
    auth: AuthService;

    constructor(
        private injector: Injector,
        private router: Router
    ) { }

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

        this.auth = this.injector.get(AuthService);
        var token = (this.auth.isLoggedIn()) ? this.auth.getAuth()!.token : null;

        if (token) {
            // save current request
            this.currentRequest = request;

            return next.handle(request)
                .do((event: HttpEvent<any>) => {
                    if (event instanceof HttpResponse) {
                        // do nothing
                    }
                })
                .catch(error => {
                    return this.handleError(error)
                });
        }
        else {
            return next.handle(request);
        }
    }

    handleError(err: any) {
        if (err instanceof HttpErrorResponse) {
            if (err.status === 401) {
                // JWT token might be expired:
                // try to get a new one using refresh token
                console.log("Token expired. Attempting refresh...");
                this.auth.refreshToken()
                    .subscribe(res => {
                        if (res) {
                            // refresh token successful
                            console.log("refresh token successful");

                            // re-submit the failed request
                            var http = this.injector.get(HttpClient);
                            http.request(this.currentRequest).subscribe(
                                (result: any) => {
                                    console.log(this.currentRequest);
                                }, (error: any) => console.error(error)
                            );
                        }
                        else {
                            // refresh token failed
                            console.log("refresh token failed");

                            // erase current token
                            this.auth.logout();

                            // redirect to login page
                            this.router.navigate(["login"]);
                        }
                    }, error => console.log(error));
            }
        }
        return Observable.throw(err);
    }
}

修改 更新了工作解决方案的代码

import { Injectable, Injector } from "@angular/core";
import { Router } from "@angular/router";
import {
    HttpClient,
    HttpHandler, HttpEvent, HttpInterceptor,
    HttpRequest, HttpResponse, HttpErrorResponse, HttpHeaders
} from "@angular/common/http";
import { AuthService } from "./auth.service";
import { Observable, Subject } from "rxjs";

@Injectable()
export class AuthResponseInterceptor implements HttpInterceptor {


    auth: AuthService;
    currentRequest: HttpRequest<any>;


    constructor(
        private injector: Injector,
        private router: Router
    ) { }


    logout() {
        this.auth.logout();
        this.router.navigate(["login"]);
    }


    intercept(
        request: HttpRequest<any>,
        next: HttpHandler): Observable<HttpEvent<any>> {


        this.auth = this.injector.get(AuthService);
        let token = (this.auth.isLoggedIn()) ? this.auth.getAuth()!.token : null;




        this.currentRequest = request;



        return next.handle(request).
            catch((error) => {
                if (error instanceof HttpErrorResponse && error.status === 401) {
                    return this.auth.refreshToken()

                        .switchMap(() => {
                            let token = (Response) ? this.auth.getAuth() : null;
                            console.log(token);

                            if (token) {
                                this.currentRequest = request.clone({
                                    setHeaders: {
                                        Authorization: `Bearer ${token.token}`
                                    }
                                });
                            }

                            return next.handle(this.currentRequest);

                        }).
                        catch((e) => {
                            this.logout();
                            console.error(e);
                            return Observable.empty();
                        });
                }

                return Observable.throw(error);

            });
    }


}

Auth.service

constructor(private http: HttpClient,
        @Inject(PLATFORM_ID) private platformId: any) {
    }

    // performs the login
    login(username: string, password: string): Observable<boolean> {
        var url = "api/token/auth";
        var data = {
            username: username,
            password: password,
            client_id: this.clientId,
            // required when signing up with username/password
            grant_type: "password",
            // space-separated list of scopes for which the token is issued
            scope: "offline_access profile email"
        };

        return this.getAuthFromServer(url, data);
    }

    // try to refresh token
    refreshToken(): Observable<boolean> {
        var url = "api/token/auth";
        var data = {
            client_id: this.clientId,
            // required when signing up with username/password
            grant_type: "refresh_token",
            refresh_token: this.getAuth()!.refresh_token,
            // space-separated list of scopes for which the token is issued
            scope: "offline_access profile email"
        };

        return this.getAuthFromServer(url, data);
    }

    // retrieve the access & refresh tokens from the server
    getAuthFromServer(url: string, data: any): Observable<boolean> {
        return this.http.post<TokenResponse>(url, data)
            .map((res) => {
                let token = res && res.token;
                // if the token is there, login has been successful
                if (token) {
                    // store username and jwt token
                    this.setAuth(res);
                    // successful login
                    return true;
                }

                // failed login
                return Observable.throw('Unauthorized');
            })
            .catch(error => {
                return new Observable<any>(error);
            });
    }

    // performs the logout
    logout(): boolean {
        this.setAuth(null);
        return true;
    }

    // Persist auth into localStorage or removes it if a NULL argument is given
    setAuth(auth: TokenResponse | null): boolean {
        if (isPlatformBrowser(this.platformId)) {
            if (auth) {
                localStorage.setItem(
                    this.authKey,
                    JSON.stringify(auth));
            }
            else {
                localStorage.removeItem(this.authKey);
            }
        }
        return true;
    }

    // Retrieves the auth JSON object (or NULL if none)
    getAuth(): TokenResponse | null {
        if (isPlatformBrowser(this.platformId)) {
            var i = localStorage.getItem(this.authKey);
            if (i) {
                return JSON.parse(i);
            }
        }
        return null;
    }

    // Returns TRUE if the user is logged in, FALSE otherwise.
    isLoggedIn(): boolean {
        if (isPlatformBrowser(this.platformId)) {
            return localStorage.getItem(this.authKey) != null;
        }
        return false;
    }

2 个答案:

答案 0 :(得分:2)

return this.auth.refreshToken(response:any)
        //response can be true or null
        let token=(response)?this.auth.getAuth():null;
        //In token we have an object of type TokenResponse
        console.log(token)
        .switchMap(() => {
            if (token) {
               this.currentRequest = request.clone({
                    setHeaders: {  //I think it's toke.token
                         Authorization: `Bearer ${token.token}`
                      }
               });
....

注意:尝试更改&#34; var&#34;为&#34;让&#34; 注2:首先你有

var token = (this.auth.isLoggedIn()) ? this.auth.getAuth()!.token : null;
//    May be  remove "!"?
let  token = (this.auth.isLoggedIn()) ? this.auth.getAuth().token : null;

答案 1 :(得分:0)

如果你想分开错误句柄,你可以做一些像

handleError(err: any) { 
    if (err instanceof HttpErrorResponse) {
        if (err.status === 401) {
            this.auth.refreshToken()
                .switchMap(res=>{  //<--switchMap, not susbcribe
                    if (res) {
                        console.log("refresh token successful");

                        // re-submit the failed request
                        var http = this.injector.get(HttpClient);
                        //Modify there this.currentRequest if was neccesary

                        return next.handle(this.currentRequest).catch(error:any=>
                        {
                            console.error(error);
                            return Observable.throw(error);
                        });
                    }
                    else {
                        console.log("refresh token failed");
                        this.auth.logout();
                        this.router.navigate(["login"]);
                    }
                })
        }
    }
    return Observable.throw(err);
}