web-dev-qa-db-ja.com

AngularJSインターセプターを使用したHTTP基本認証ダイアログの防止

RESTful APIを使用してAngularJS(1.2.16)Webアプリを構築しています。認証情報が無効であるか存在しないリクエストに対して、401 Unauthorized応答を送信したいと思います。これを行うと、HTTPインターセプターが存在する場合でも、AJAX要求がAngularJSを介して行われると、ブラウザーが提示する基本的な「認証が必要です」というダイアログが表示されます。インターセプターが実行されるafterそのダイアログ。これは、何か役に立つことをするには遅すぎます。

具体例:

認証トークンが存在しない場合、私のバックエンドAPIは/api/thingsに対して401を返します。素敵でシンプル。

AngularJSアプリ側では、 docs を調べて、configブロックに次のようなインターセプターを設定しました。

$httpProvider.interceptors.Push(['$q', function ($q) {
  return {
    'responseError': function (rejection) {
      if (rejection.status === 401) {
        console.log('Got a 401')
      }
      return $q.reject(rejection)
    }
  }
}])

アプリをロードし、認証トークンを削除して、AJAXへの/api/thingsへの呼び出しを実行すると(うまくいけば、上記のインターセプターをトリガーするため)、次のようになります。

Authentication Required

そのダイアログをキャンセルすると、表示したい「Got a 401」のconsole.log出力が表示されますそのダイアログの代わりに

Got a 401

明らかに、インターセプターは機能していますが、インターセプトが遅すぎます!

このような状況でAngularJSを使用した認証に関するWeb上の投稿を数多く見かけますが、それらはすべてHTTPインターセプターを使用しているようですが、基本的な認証ダイアログのポップアップについては触れていません。その外観について私が持っていたいくつかの誤った考えには、

  • 応答にContent-Type: application/jsonヘッダーがありませんか?いいえ、あります。
  • 約束の拒否以外のものを返す必要がありますか?そのコードは、何が返されても、常にダイアログの後に実行されます。

いくつかのセットアップ手順がないか、インターセプターを誤って使用していますか?

24
Collin Allen

理解した!

トリックは、Basic以外の値のWWW-Authenticate応答ヘッダーを送信することでした。次に、基本的な$httpインターセプター、または angular-http-auth のようなより賢いもので401をキャプチャできます。

12
Collin Allen

この問題はSpring Boot Security(HTTP basic)と一緒に発生しました。Angular 1.3以降、ポップアップが表示されないようにするには、$httpProvider.defaults.headers.common["X-Requested-With"] = 'XMLHttpRequest';を設定する必要があります。

9
Busata

今後の参考用

401エラーを処理しようとしたときに、この解決策を思いつきました。 Basicx-Basicなどに書き換えるオプションがなかったため、Angularを使用してクライアント側で処理することにしました。

ログアウトを開始するときは、まず、偽のユーザーに不適切な要求を行って、現在キャッシュされている資格情報を破棄してください。

私はこの関数にリクエストを実行させています(非同期クエリを無効にしてjqueryの$.ajaxを使用しています):

function authenticateUser(username, hash) {
    var result = false;
    var encoded = btoa(username + ':' + hash);

    $.ajax({
        type: "POST",
        beforeSend: function (request) {
            request.setRequestHeader("Authorization", 'Basic ' + encoded);
        },
        url: "user/current",
        statusCode: {
            401: function () {
                result = false;
            },
            200: function (response) {
                result = response;
            }
        },
        async: false
    });

    return result;
}

したがって、ユーザーをログアウトしようとすると、次のようになります。

//This will send a request with a non-existant user.
//The purpose is to overwrite the cached data with something else
accountServices.authenticateUser('logout','logout');

//Since setting headers.common.Authorization = '' will still send some
//kind of auth data, I've redefined the headers.common object to get
//rid of the Authorization property
$http.defaults.headers.common = {Accept: "application/json, text/plain, */*"};
1
Alex Szabó