Skip to main content
 首页 » 编程设计

AngularJS之http 拦截器之在 token 刷新后重新发送所有请求

2024年11月24日6飞鱼

我有一个 angular 应用程序,它有时会为每个状态执行多个 $http.get 请求。该应用程序使用 JWT 进行带有刷新 token 的用户身份验证。 API 服务器发送 401在由于身份验证错误而失败的每个请求上。
我做了一个 http interceptor在 401 错误时请求带有刷新 token 的新 token ,然后重新发送原始请求。

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

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

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

请您参考如下方法:

您的拦截器需要跟踪它是否有“正在运行”的身份验证请求。它可以通过保留对身份验证请求返回的 promise 的引用来做到这一点。如果有一个请求正在运行并且你得到另一个 401,只需使用缓存的 promise 而不是启动一个新的请求。此外,您应该考虑添加逻辑来解决“/api/auth/refresh”本身返回 401 的情况。

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