AngularJS - http interceptor - resend all request after token refresh

前端 未结 2 1638
谎友^
谎友^ 2020-12-08 10:49

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

相关标签:
2条回答
  • 2020-12-08 11:11

    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. Also, you should consider adding logic to account for the case when '/api/auth/refresh' itself returns a 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);
            }
        };
    });
    
    0 讨论(0)
  • 2020-12-08 11:30

    The solution of Joe Enzminger is great. But I had a few issues with the callback as it didn't execute. Then I noticed a little typo in inflightAuthRequest/inFlightAuthRequest.

    My complete solution is now:

    (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);
                    }
                }
            }
        }
    
    })();
    
    0 讨论(0)
提交回复
热议问题