简体   繁体   English

Angular - JWT刷新令牌

[英]Angular - JWT Refresh Token

After a long time I didn't find an approach about refresing tokens TTL: 30 minutes Refresh TTL: 2 Weeks 很长一段时间后,我没有找到关于刷新令牌的方法TTL:30分钟刷新TTL:2周

If I refresh the page after 45 minutes innactive then I make a getAccessToken() function to send the expired token and then send me back a refreshed token. 如果我在45分钟后刷新页面无效,那么我创建一个getAccessToken()函数来发送过期的令牌,然后再发回一个刷新的令牌。 The big problem is that if my page makes more than 1 ajax request then if the first request invalidate my token the second request force my to relogin becauce it send empty token 最大的问题是,如果我的页面发出超过1个ajax请求,那么如果第一个请求使我的令牌无效,则第二个请求强制我重新登录,因为它发送空令牌

@NgModule({
providers: [
    {
        provide: AuthHttp,
        useFactory: authHttpServiceFactory,
        deps: [Http, RequestOptions, Router]
    }
]
})

export function authHttpServiceFactory(http: Http, options: RequestOptions, router: Router) {
return new AuthHttp(new AuthConfig({
    tokenName: 'token',
    tokenGetter: (() => getAccessToken(http,router)),
    //tokenGetter: (() => localStorage.getItem('JWToken')),
    globalHeaders: [{'Content-Type': 'application/json'}],
    noJwtError: true,
}), http, options);
}


function getAccessToken(http: Http, router:Router): Promise<string> {

let jwtHelper: JwtHelper = new JwtHelper();

let accessToken = localStorage.getItem('JWToken');

if( accessToken == '' || !accessToken || accessToken == undefined || accessToken == null){
    router.navigate(['./admin/login']);
    return;
}


if (jwtHelper.isTokenExpired(accessToken)) {
    return new Promise((resolve, reject) => {
        let refreshTokenService: RefreshTokenService = new RefreshTokenService(http);
        refreshTokenService.refreshToken(accessToken).subscribe((res: any) => {
            res = res.json();

            if(res.token) {
                localStorage.setItem('JWToken', res.token);
                resolve(res.token);

            }else{
                localStorage.removeItem('JWToken');

                router.navigate(['./admin/login']);
            }
        });
    });
} else {
    return Promise.resolve(accessToken);
}
}

I want the requests wait for the response of the first request 我希望请求等待第一个请求的响应

  • Send all http requests in your app using a special service 使用特殊服务在您的应用中发送所有http请求
  • Store 401 responses in a buffer inside this service together with an observable that you'll return to the caller. 将401响应存储在此服务内的缓冲区中,同时将一个observable存储到您将返回给调用者。 The first 401 sends the token refresh request 第一个401发送令牌刷新请求
  • When you've got he new token, repeat all the requests in the buffer with the new token and call their observables with the new response. 当你有新的令牌时,用新令牌重复缓冲区中的所有请求,并使用新的响应调用它们的observable。

And this is the injector for the new httpClient library 这是新的httpClient库的注入器

import {Injectable, Injector} from "@angular/core";
import {HttpEvent, HttpHandler, HttpInterceptor, HttpResponse} from "@angular/common/http";
import {HttpRequest} from "@angular/common/http";
import {Observable} from "rxjs/Observable";
import {SiteService} from "../services/site.service";
import {Router} from "@angular/router";
import {LoadingService} from "../../components/loading/loading.service";
import {AuthenticationService} from "../services/authentication.service";

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

constructor(private router: Router,
            private siteService: SiteService,
            private loadingService: LoadingService,
            private injector: Injector) {
}



private fixUrl(url: string) {
    if (url.indexOf('http://') >= 0 || url.indexOf('https://') >= 0)
        return url;
    else
        return this.siteService.apiDomain() + url;
}

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

    let clonedRequest;
    if ( this.siteService.getJWToken() !== null ) {
        clonedRequest = req.clone({
            headers: req.headers.set('Authorization', 'Bearer ' + this.siteService.getJWToken()),
            url: this.fixUrl(req.url)
        });
    } else {
        clonedRequest = req.clone({
            url: this.fixUrl(req.url)
        });
    }

    let authenticationService = this.injector.get(AuthenticationService);

    this.loadingService.start();
    const started = Date.now();
    return next.handle(clonedRequest)
        .do(event => {
            if (event instanceof HttpResponse) {

                const elapsed = Date.now() - started;
                console.log('%c Request for ' + this.fixUrl(req.urlWithParams) + ' took ' + elapsed + ' ms.', 'background: #222; color: yellow');
            }
        })
        ._finally(() => {
            this.loadingService.stop();
        })
        .catch((res) => {
            if ((res.status === 401 || res.status === 403) && res.error.error === 'token_expired') {
                this.loadingService.start();
                return authenticationService.refreshToken().flatMap((data: any) => {
                    this.loadingService.stop();
                    if (data.token !== '') {
                        this.siteService.setCurrentUser(data.user);
                        this.siteService.setCurrentUserPermissions(data.permissions);
                        this.siteService.setJWToken(data.token);
                    } else {
                        this.siteService.removeCurrentUser();
                        this.siteService.removeCurrentUserPermissions();
                        this.siteService.removeJWToken();
                        this.router.navigate(['./auth/login']);
                        return Observable.throw(res);
                    }
                    let clonedRequestRepeat = req.clone({
                        headers: req.headers.set('Authorization', 'Bearer ' + this.siteService.getJWToken()),
                        url: this.fixUrl(req.url)
                    });
                    return next.handle(clonedRequestRepeat).do(event => {
                        if (event instanceof HttpResponse) {

                            const elapsed = Date.now() - started;
                            console.log('%c Request for ' + req.urlWithParams + ' took ' + elapsed + ' ms.', 'background: #222; color: yellow');
                        }
                    });
                })
            } else if (res.status === 400 && res.error.error === 'token_not_provided') {
                this.router.navigate(['./auth/login']);
                return Observable.throw(res);
            } else if (res.status === 401 && res.error.error === 'token_invalid') {
                this.router.navigate(['./auth/login']);
                return Observable.throw(res);
            } else {
                return Observable.throw(res);
            }

        });

}
}

And do not forget to send cache (browser) headers to the backend response for at least some seconds. 并且不要忘记将缓存(浏览器)标头发送到后端响应至少几秒钟。

function getAccessToken(http: Http, router: Router, refreshTokenService: RefreshTokenService): Promise<string> {

let jwtHelper: JwtHelper = new JwtHelper();

let accessToken = localStorage.getItem('JWToken');

if (accessToken == '' || !accessToken || accessToken == undefined || accessToken == null) {
    router.navigate(['./admin/login']);
    return;
}

if (jwtHelper.isTokenExpired(accessToken)) {

    let waitPeriod = (!refreshTokenService.wait);

    refreshTokenService.wait = true;

    return new Promise((resolve, reject) => {

        if (waitPeriod) {
            refreshTokenService.refreshToken(accessToken).subscribe((res: any) => {
                res = res.json();

                if (res.token) {
                    localStorage.setItem('JWToken', res.token);
                    resolve(res.token);
                    refreshTokenService.wait = false;
                } else {
                    localStorage.removeItem('JWToken');
                    router.navigate(['./admin/login']);
                }

            });
        } else {
            let interval = setInterval(function () {
                if(refreshTokenService.wait == false) {
                    resolve(localStorage.getItem('JWToken'));
                    clearInterval(interval);
                }
            }, 500);
        }

    });
} else {
    return Promise.resolve(accessToken);
}
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM