首页 文章

刷新身份验证令牌时处理多个未经授权的请求

提问于
浏览
2

使用JWT进行API验证的Angular应用程序在API调用返回 401 "Unauthorized" 时启动登录对话框,让用户输入其凭据并获得新的有效JWT . 然后,应用程序重试失败的未授权请求并保持流程 .

此处列出的代码基于Chris Clarke的this solution .

.config(['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push(['$q', '$location', '$injector', function ($q, $location, $injector) {
    return {
        'responseError': function(response) {
            // Keep the response waiting until we get new JWT
            var deferred = $q.defer();
            if (response.status === 401 && response.data.error && response.data.error.message.toLowerCase() === "unauthorized") {
                // JWT has expired
                // Open login dialog
                var cslAuth = $injector.get('cslAuth');
                if (cslAuth.isLoggedIn()) {
                    // Logout user, next pending request will not trigger auth dialog
                    cslAuth.logout();
                    $injector.get('ngDialog').openConfirm({
                        template: 'web_app/views/login.html',
                        className: 'ngdialog-theme-default',
                        showClose: false,
                        controller: 'LoginCtrl',
                        cache: false
                    })
                    .then(
                        function(value) {
                            // JWT has been refreshed. Try pending request again
                            var config = response.config;
                            // Inject the new token in the Auth header
                            config.headers.Authentication = cslAuth.getTokenHeader();
                            $injector.get("$http")(config).then(
                                function(response){
                                    deferred.resolve(response);
                                },
                                function(response) {
                                    deferred.reject();
                                }
                            );
                        },
                        function(value) {
                            deferred.reject();
                        }
                    );
                }
            } else {
                return $q.reject(response);
            }
            // Return a promise while waiting for auth refresh
            return deferred.promise;
        }
    }
}])
}])

问题是当过期令牌有多个请求时 . 第一个返回的应该触发登录对话框并获取新令牌 . 但是如何让其他待处理请求等到新令牌可用?可以设置标志以告知所有后续传入的响应,即正在请求新令牌 . 可以返回promise,并且所有配置对象都可以存储在Service中的数组中 . 当新令牌可用时,可以重试所有等待请求 . 但是在新令牌可用后返回未授权请求会发生什么?它们将触发新的登录对话框 .

一些注意事项:

  • This answer为相关问题提供了解决方案,但由于此处涉及新的登录,我无法看到如何使解决方案适应这种情况 .

  • 这不是自动更新令牌的选项 . 令牌将有8小时到期(工作会话)并且必须进行新登录 .

  • 在配置对象中注入服务( cslAuth$http 这里)是否安全?我的代码正在运行,但我已经读过他们现在无法完全准备好了 .

1 回答

  • 2

    此代码以两种方式改进了问题中发布的代码:

    • 保留一个pendingRequests数组,然后在成功完成令牌刷新后重试

    • 在401上,它会判断所使用的身份验证令牌是否与当前令牌不同,以便在不要求登录的情况下再次重试请求 . 这解决了pendingRequests数组中未包含的请求问题 .

    码:

    / HTTP Interceptors
    .config(['$httpProvider', function($httpProvider) {
        $httpProvider.interceptors.push(['$q', '$location', '$injector', function ($q, $location, $injector) {
            var pendingRequests = [];
            function retryRequest(deferred, config, cslAuth) {
                config.headers.Authentication = cslAuth.getTokenHeader();
                $injector.get("$http")(config).then(
                    function(response){
                        deferred.resolve(response);
                    },
                    function(response) {
                        deferred.reject();
                    }
                );
            }
            return {
                'responseError': function(response) {
                    switch (response.status) {
                        case 401: // JWT has expired
                            // To keep the response waiting until we get new JWT
                            var deferred = $q.defer();
                            var cslAuth = $injector.get('cslAuth');
                            // Check if a new token exists. Then retry the request with new token
                            if (response.config.headers.Authentication != cslAuth.getTokenHeader()) {
                                retryRequest(deferred, response.config, cslAuth);
                                // Return a promise while waiting
                                return deferred.promise;
                            }
                            // Open login dialog
                            if (cslAuth.isLoggedIn()) {
                                // Logout user, next pending request will not trigger auth dialog
                                cslAuth.logout();
                                $injector.get('ngDialog').openConfirm({
                                    template: 'web_app/views/login-inner.html',
                                    className: 'ngdialog-theme-default',
                                    showClose: false,
                                    controller: 'LoginCtrl'
                                })
                                .then(
                                    function(value) {
                                        // JWT has been refreshed. Try pending requests again
                                        for (var i = 0; i < pendingRequests.length; i++) {
                                            retryRequest(pendingRequests[i].deferred, pendingRequests[i].config, cslAuth);
                                        }
                                    },
                                    function(value) {
                                        pendingRequests[i].deferred.reject();
                                    }
                                );
                            }
                            // Return a promise while waiting for auth refresh
                            pendingRequests.push({'deferred': deferred, 'config': response.config});
                            return deferred.promise;
                            break;
                        default: // What happened?
                            return $q.reject(response);
                            break;
                    }
                }
            }
        }])
    }])
    

相关问题