Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/angularjs/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/unit-testing/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
AngularJS-http拦截器-令牌刷新后重新发送所有请求_Angularjs - Fatal编程技术网

AngularJS-http拦截器-令牌刷新后重新发送所有请求

AngularJS-http拦截器-令牌刷新后重新发送所有请求,angularjs,Angularjs,我有一个angular应用程序,它有时会在每个州执行多个$http.get请求。应用程序使用JWT进行用户身份验证,并使用刷新令牌。API服务器在每个由于身份验证错误而失败的请求上发送401。 我制作了一个http拦截器,它在401错误时请求一个带有刷新令牌的新令牌,然后重新发送原始请求 问题是,如果一个状态发出例如2$http.get请求,并且两个请求都得到401响应,那么我会将访问令牌续订两次。显然,我只想刷新令牌一次,但我仍然想重新发送两个失败的请求 这是否可以实现?如果可以,如何实现 a

我有一个angular应用程序,它有时会在每个州执行多个$http.get请求。应用程序使用JWT进行用户身份验证,并使用刷新令牌。API服务器在每个由于身份验证错误而失败的请求上发送
401
。 我制作了一个
http拦截器
,它在401错误时请求一个带有刷新令牌的新令牌,然后重新发送原始请求

问题是,如果一个状态发出例如2$http.get请求,并且两个请求都得到401响应,那么我会将访问令牌续订两次。显然,我只想刷新令牌一次,但我仍然想重新发送两个失败的请求

这是否可以实现?如果可以,如何实现

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);
        }
    };
});

您的拦截器需要跟踪是否有“正在运行”的身份验证请求。它可以通过保留对身份验证请求返回的承诺的引用来实现这一点。如果有一个请求正在进行中,而您又得到了另一个401,只需使用缓存的承诺,而不是启动一个新的请求。此外,您应该考虑添加逻辑来解释“/API/AUTH/刷新”本身返回401。< /P>的情况。
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);
        }
    };
});

乔·恩兹明格的解决方案很好。但是我对回调有一些问题,因为它没有执行。 然后我注意到inflightAuthRequest/inflightAuthRequest中有一点输入错误

我现在的完整解决方案是:

(function() {
'use strict';
    angular.module('app.lib.auth', []);
    angular.module('app.lib.auth')
        .factory('authService', authService);
    angular.module('app.lib.auth')
        .factory('AuthInterceptor', AuthInterceptor);

    function authService($window) {
        return {
            getToken: function() {
                return $window.localStorage.getItem('JWT');
            },
            getRefreshToken: function() {
                return $window.localStorage.getItem('Refresh-JWT');
            },
            setRefreshToken: function(token) {
                $window.localStorage.setItem('Refresh-JWT', token);
            },
            setToken: function(token) {
                $window.localStorage.setItem('JWT', token);
            },
            clearAllToken: function(){
                $window.localStorage.removeItem('JWT');
                $window.localStorage.removeItem('Refresh-JWT');
            },
            clearToken: function(){
                $window.localStorage.removeItem('JWT');
            },
            isLoggedIn: function() {
                if ($window.localStorage.getItem('JWT') === null) {
                    return false;
                }
                else {
                    return true;
                }
            },
            toLogin: function(){
                $window.location.href = "http://" + $window.location.host + "/tprt/login";
            }
        }
    }

    function AuthInterceptor($q, $injector, authService) {
        var inFlightAuthRequest = null;
        return {
            request : function(config) {
                config.headers = config.headers || {};
                if(authService.getToken()){
                    config.headers['Authorization'] = authService.getToken();
                }
                return config;
            },
            responseError : function(response) {
                if(response.config.url == URLS.api_refresh_token){
                    console.log(JSON.stringify(response));
                    authService.clearAllToken();
                    authService.toLogin();
                }else{

                    switch (response.status) {
                    case 401:
                        authService.clearToken();
                        var deferred = $q.defer();
                        if (!inFlightAuthRequest) {
                            inFlightAuthRequest = $injector.get("$http").post(
                                    URLS.api_refresh_token, { 
                                        refreshtoken : authService.getRefreshToken()
                                    });
                        }
                        inFlightAuthRequest.then(function(r) {
                            inFlightAuthRequest = null;
                            console.log(JSON.stringify(r));
                            authService.setToken(r.data.accesstoken);
                            $injector.get("$http")(response.config).then(function(resp) {
                                deferred.resolve(resp);
                            }, function(resp) {
                                deferred.reject(resp);
                            });
                        }, function(error) {
                            inFlightAuthRequest = null;
                            deferred.reject();
                            authService.clearAllToken();
                            authService.toLogin();
                            return;
                        });
                        return deferred.promise;
                        break;
                    default:
                        return $q.reject(response);
                    break;
                    }
                    return response || $q.when(response);
                }
            }
        }
    }

})();

你能发布截取器的代码片段以及如何重新发送请求吗?有帮助吗?@KevinHakanson:我也不知道这个库。
事件:auth login required
仍将触发两次,使应用程序刷新令牌两次。我错了吗?我遗漏了什么?这个解决方案非常棒,因为它将所有请求返回控制器,并在控制器中由成功和错误函数进行解释。非常感谢您提供此解决方案!在访问令牌过期时工作良好,但在刷新令牌过期时出错。有人观察到了同样的行为吗?@SAtahAhmedKhan:它失败了,因为监听器还截获了刷新令牌过期时刷新端点返回的消息。因此,不会执行失败代码。相反,刷新请求在inflightAuthRequest承诺重新执行时排队。要解决这个问题,应该检查response.url。这是一个“安全”的解决方案吗?有权访问控制台(或恶意浏览器插件)的人是否可以通过获取服务并调用authService.getRefreshToken()来访问刷新令牌?@MMK:此代码截获所有401个请求,但如果刷新令牌过期,则所有等待的请求都将失败,但是上面的代码将调度401个refresh失败,以便像所有其他请求一样重新执行。所以,您应该检查response.url以确定401是来自常规请求还是指向令牌刷新端点。若您从令牌刷新端点获得401,那个么您应该将失败返回给所有等待的请求。