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
への呼び出しを実行すると(うまくいけば、上記のインターセプターをトリガーするため)、次のようになります。
そのダイアログをキャンセルすると、表示したい「Got a 401」のconsole.log
出力が表示されますそのダイアログの代わりに:
明らかに、インターセプターは機能していますが、インターセプトが遅すぎます!
このような状況でAngularJSを使用した認証に関するWeb上の投稿を数多く見かけますが、それらはすべてHTTPインターセプターを使用しているようですが、基本的な認証ダイアログのポップアップについては触れていません。その外観について私が持っていたいくつかの誤った考えには、
Content-Type: application/json
ヘッダーがありませんか?いいえ、あります。いくつかのセットアップ手順がないか、インターセプターを誤って使用していますか?
理解した!
トリックは、Basic
以外の値のWWW-Authenticate
応答ヘッダーを送信することでした。次に、基本的な$http
インターセプター、または angular-http-auth のようなより賢いもので401をキャプチャできます。
この問題はSpring Boot Security(HTTP basic)と一緒に発生しました。Angular 1.3以降、ポップアップが表示されないようにするには、$httpProvider.defaults.headers.common["X-Requested-With"] = 'XMLHttpRequest';
を設定する必要があります。
今後の参考用
401
エラーを処理しようとしたときに、この解決策を思いつきました。 Basic
をx-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, */*"};