2

API 認証に JWT を使用する Angular アプリは、API への呼び出しが返されたときにログイン ダイアログを起動し401 "Unauthorized"、ユーザーが資格情報を入力して新しい有効な JWT を取得できるようにします。その後、アプリは失敗した無許可の要求を再試行し、フローを維持します。

ここにリストされているコードは、Chris Clarke によるこのソリューションに基づいています。

.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 の配列に格納できます。新しいトークンが利用可能になると、待機中のすべてのリクエストを再試行できます。しかし、新しいトークンが利用可能になった後に返される無許可のリクエストはどうなるでしょうか? それらは、新しいログイン ダイアログをトリガーします。

追加のメモ:

  • この回答は関連する問題の解決策を提供しますが、ここには新しいログインが含まれているため、このケースに解決策を適応させる方法がわかりません。

  • トークンを自動更新するオプションではありません。トークンの有効期限は 8 時間 (作業セッション) で、新しいログインが必須です。

  • 構成オブジェクトにサービス (cslAuthおよびここ)を挿入しても安全ですか? $http私のコードは機能していますが、現時点では完全に準備ができていないことを読みました。
4

1 に答える 1

3

このコードは、質問に投稿されたものを 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;
                }
            }
        }
    }])
}])
于 2015-11-21T13:32:26.200 に答える