AngularJS - HTTP拦截 - 重发令牌刷新毕竟要求 [英] AngularJS - http interceptor - resend all request after token refresh

查看:195
本文介绍了AngularJS - HTTP拦截 - 重发令牌刷新毕竟要求的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个角度应用程序,它有时确实每个州的多个$ http.get请求。该应用程序usees智威汤逊与刷新令牌用户认证。 API服务器发送 401 对每一个失败,因为身份验证错误的请求。
我做了一个 HTTP拦截请求一个新的令牌与401错误的刷新令牌并重新发送原始请求后。

问题是,如果一国如2 $ http.get请求,都得到401的响应,那么我续订访问令牌的两倍。很显然,我只想刷新令牌一次,但我还是想重新发送BOTH失败的请求。

这是可以实现的,如果是这样如何?

  app.factory('AuthInterceptor',函数($ Q $喷油器,RESOURCE_URL,API_BASE,authService){
    返回{
        要求:功能(配置){
            config.headers = config.headers || {};
            如果(authService.getAccessToken()){
                如果(config.url.substring(0,RESOURCE_URL.length)!== RESOURCE_URL){
                    config.headers.Authorization ='承载'+ authService.getAccessToken();
                }
            }
            返回配置;
        },
        responseError:功能(响应){
            开关(response.status){
                案例401:
                    变种推迟= $ q.defer();
                    。$ injector.get($ HTTP)后(API_BASE +'/ API / auth /中刷新',{refreshtoken:authService.getRefreshToken()}),然后(函数(R){
                        如果(r.data.data.accesstoken&安培;&安培; r.data.data.refreshtoken和放大器;&安培; r.data.data.expiresin){
                            authService.setAccessToken(r.data.data.accesstoken);
                            authService.setRefreshToken(r.data.data.refreshtoken);
                            authService.setExpiresIn(r.data.data.expiresin);
                            $ injector.get($ HTTP)(response.config)。然后(功能(RESP){
                                deferred.resolve(RESP);
                            },功能(RESP){
                                deferred.reject();
                            });
                        }其他{
                            deferred.reject();
                        }
                    },函数(响应){
                        deferred.reject();
                        authService.clear();
                        。$ injector.get($状态)去('guest.login');
                        返回;
                    });
                    返回deferred.promise;
                    打破;
                默认:
                    authService.clear();
                    。$ injector.get($状态)去('guest.login');
                    打破;
            }
            返回响应|| $ q.when(响应);
        }
    };
});


解决方案

您拦截器需要跟踪它是否具有飞行中的认证请求。它可以通过保持由认证请求返回的承诺一个参照。如果在飞行的请求,你会得到另一个401,只使用缓存的承诺,而不是发起一个新的请求。

  app.factory('AuthInterceptor',函数($ Q $喷油器,RESOURCE_URL,API_BASE,authService){
    VAR inFlightAuthRequest = NULL;
    返回{
        要求:功能(配置){
            config.headers = config.headers || {};
            如果(authService.getAccessToken()){
                如果(config.url.substring(0,RESOURCE_URL.length)!== RESOURCE_URL){
                    config.headers.Authorization ='承载'+ authService.getAccessToken();
                }
            }
            返回配置;
        },
        responseError:功能(响应){
            开关(response.status){
                案例401:
                    变种推迟= $ q.defer();
                    如果(!inFlightAuthRequest){
                        。inflightAuthRequest = $ injector.get($ HTTP)后(API_BASE +'/ API / auth /中刷新',{refreshtoken:authService.getRefreshToken()});
                    }
                    inflightAuthRequest.then(函数(R){
                        inflightAuthRequest = NULL;
                        如果(r.data.data.accesstoken&安培;&安培; r.data.data.refreshtoken和放大器;&安培; r.data.data.expiresin){
                            authService.setAccessToken(r.data.data.accesstoken);
                            authService.setRefreshToken(r.data.data.refreshtoken);
                            authService.setExpiresIn(r.data.data.expiresin);
                            $ injector.get($ HTTP)(response.config)。然后(功能(RESP){
                                deferred.resolve(RESP);
                            },功能(RESP){
                                deferred.reject();
                            });
                        }其他{
                            deferred.reject();
                        }
                    },函数(响应){
                        inflightAuthRequest = NULL;
                        deferred.reject();
                        authService.clear();
                        。$ injector.get($状态)去('guest.login');
                        返回;
                    });
                    返回deferred.promise;
                    打破;
                默认:
                    authService.clear();
                    。$ injector.get($状态)去('guest.login');
                    打破;
            }
            返回响应|| $ q.when(响应);
        }
    };
});

I have an angular app which sometimes does multiple $http.get requests per state. The app usees JWT for user auth with refresh tokens. The API server sends 401 on every request that failed because of auth error. I've made an http interceptor that requests a new token with the refresh token on 401 errors and after that resends the original request.

The problem is, if a state makes for example 2 $http.get requests and both get 401 response then I renew the access token twice. Obviously I only want to refresh the token once, BUT I still want to resend BOTH failed requests.

Is this achievable and if so how?

app.factory('AuthInterceptor', function($q, $injector, RESOURCE_URL, API_BASE, authService) {
    return {
        request: function(config) {
            config.headers = config.headers || {};
            if (authService.getAccessToken()) {
                if (config.url.substring(0, RESOURCE_URL.length) !== RESOURCE_URL) {
                    config.headers.Authorization = 'Bearer ' + authService.getAccessToken();
                }
            }
            return config;
        },
        responseError: function(response) {
            switch (response.status) {
                case 401:
                    var deferred = $q.defer();
                    $injector.get("$http").post(API_BASE + '/api/auth/refresh', {refreshtoken: authService.getRefreshToken()}).then(function(r) {
                        if (r.data.data.accesstoken && r.data.data.refreshtoken && r.data.data.expiresin) {
                            authService.setAccessToken(r.data.data.accesstoken);
                            authService.setRefreshToken(r.data.data.refreshtoken);
                            authService.setExpiresIn(r.data.data.expiresin);
                            $injector.get("$http")(response.config).then(function(resp) {
                                deferred.resolve(resp);
                            },function(resp) {
                                deferred.reject();
                            });
                        } else {
                            deferred.reject();
                        }
                    }, function(response) {
                        deferred.reject();
                        authService.clear();
                        $injector.get("$state").go('guest.login');
                        return;
                    });
                    return deferred.promise;
                    break;
                default:
                    authService.clear();
                    $injector.get("$state").go('guest.login');
                    break;
            }
            return response || $q.when(response);
        }
    };
});

解决方案

Your interceptor needs to keep track of whether or not it has an authentication request "in flight". It can do this by keeping a reference to the promise returned by the authentication request. If there is a request in flight and you get another 401, just use that cached promise instead of initiating a new request.

app.factory('AuthInterceptor', function($q, $injector, RESOURCE_URL, API_BASE, authService) {
    var inFlightAuthRequest = null;
    return {
        request: function(config) {
            config.headers = config.headers || {};
            if (authService.getAccessToken()) {
                if (config.url.substring(0, RESOURCE_URL.length) !== RESOURCE_URL) {
                    config.headers.Authorization = 'Bearer ' + authService.getAccessToken();
                }
            }
            return config;
        },
        responseError: function(response) {
            switch (response.status) {
                case 401:
                    var deferred = $q.defer();
                    if(!inFlightAuthRequest) {
                        inflightAuthRequest = $injector.get("$http").post(API_BASE + '/api/auth/refresh', {refreshtoken: authService.getRefreshToken()});
                    }
                    inflightAuthRequest.then(function(r) {
                        inflightAuthRequest = null;
                        if (r.data.data.accesstoken && r.data.data.refreshtoken && r.data.data.expiresin) {
                            authService.setAccessToken(r.data.data.accesstoken);
                            authService.setRefreshToken(r.data.data.refreshtoken);
                            authService.setExpiresIn(r.data.data.expiresin);
                            $injector.get("$http")(response.config).then(function(resp) {
                                deferred.resolve(resp);
                            },function(resp) {
                                deferred.reject();
                            });
                        } else {
                            deferred.reject();
                        }
                    }, function(response) {
                        inflightAuthRequest = null;
                        deferred.reject();
                        authService.clear();
                        $injector.get("$state").go('guest.login');
                        return;
                    });
                    return deferred.promise;
                    break;
                default:
                    authService.clear();
                    $injector.get("$state").go('guest.login');
                    break;
            }
            return response || $q.when(response);
        }
    };
});

这篇关于AngularJS - HTTP拦截 - 重发令牌刷新毕竟要求的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆