ある時点で、アプリケーションは認可が必要になります。これは、異なるレベルのアクセスがWebサイト(またはその他のもの)で異なる動作をすることを意味します。データの表示から、ユーザーのグループがアクセスできない領域全体まで、何でもあり得ます。
シングルページアプリケーション(SPA)ではない従来のアプリケーションでは、クレームまたはロールはデータまたはアプリケーションの領域に関連付けられており、ユーザーがこのロールまたはクレームを持っているか持っていないかのいずれかです。SPAでも同じですが、大きな免責事項があります。SPAはブラウザにダウンロードされます。この時点で、ブラウザはコードを完全に制御しています。悪意のある人がコードを変更して自分の目的に使用することができます。
SPAは保護できないため、SPA内の認証と認可は単なるユーザーエクスペリエンスです。すべての意味のあるセキュリティはWebサーバーで実行する必要があります。この記事はAPIを攻撃から保護することについては説明していません。Pluralsightのビデオまたはサーバーテクノロジーのセキュリティに対応するペーパーを読むことをお勧めします。
この記事の目的は、Angular 1.x SPAに認可ユーザーエクスペリエンスを追加した方法を示すことです。
セキュリティスコープ
認可が必要なUIの3つの領域を特定しました:要素(HTML)、ルート、およびデータ。
念のため、SPAの保護はサーバーの保護の代わりにはなりません。クライアント上の権限は、単に正直な人々を正直に保つため、およびユーザーに良い体験を提供するためのものです。
3つの領域の詳細:
要素
特定のHTML要素を非表示にする必要があります。ラベル、データを含むテーブル、ボタン、またはページ上の任意の要素である可能性があります。
ルート
ルート全体を非表示にしたいでしょう。特定の場合には、ユーザーがビューにアクセスすることを望みません。ルートを保護することで、ユーザーはビューにナビゲートできません。代わりに、「このビューにナビゲートする権限がありません」というメッセージが表示されます。
データ
ビュー内の要素を非表示にするだけでは不十分な場合があります。注意深いユーザーはソースを表示して、HTMLソースに隠されたデータを見たり、ブラウザにストリーミングされるのを見たりすることができます。私たちが望むのは、データが最初に取得されないことです。
セキュリティの追加は難しいです。最初は、HTTP API(クライアント上)でアクセスを制限しようとしました。これは機能しないことにすぐに気付きました。ユーザーはデータに直接アクセスできない場合がありますが、これはデータに間接的にアクセスできないことを意味しません。HTTP APIレイヤー(通常、アプリケーションの最も低いレイヤーの1つ)では、呼び出しのコンテキストを判断できないため、セキュリティ上の懸念を適用できません。
以下にコードサンプルを提供しました:
コード
認可チェックコード用のサービスを作成しました。これは認可の中核です。すべての認可リクエストはこのサービスを使用して、ユーザーが特定のアクションに対して認可されているかどうかを確認します。
angular.module('services')
.service('AuthorizationContext',function(_, Session){
this.authorizedExecution = function(key, action){
//Looking for the claim key that was passed in. If it exists in the claim set, then execute the action.
Session.claims(function(claims){
var claim = findKey(key, claims);
//If Claim was found then execute the call.
//If it was not found, do nothing
if(claim !== undefined){
action();
}
});
};
this.authorized = function(key, callback){
//Looking for the claim key that was passed in. If it exists in the claim set, then execute the action.
Session.claims(function(claims){
var claim = findKey(key, claims);
//If they don't have any security key, then move forward and authorization.
var valid = claim !== undefined;
callback(valid);
});
};
//this.agencyViewKey = '401D91E7-6EA0-46B4-9A10-530E3483CE15';
function findKey(key, claims){
var claim = _.find(claims, function(item){
return item.value === key;
});
return claim;
}
});
認可ディレクティブ
認可ディレクティブは、特定のレベルのアクセスを持たないユーザーから非表示にしたいHTML要素に適用できます。ユーザーがアクセストークンをクレームの一部として持っている場合、要素を見ることが許可されます。持っていない場合は、非表示になります。
angular.module('directives')
.directive('authorize', ['$compile', 'AuthorizationContext', function($compile, AuthorizationContext) {
return {
restrict: 'A',
replace: true,
//can't have isolated the scope in a shared directive
link:function ($scope, element, attributes) {
var securityKey = attributes.authorize;
AuthorizationContext.authorized(securityKey, function(authorized){
var el = angular.element(element);
el.attr('ng-show', authorized);
//remove the attribute, otherwise it creates an infinite loop.
el.removeAttr('authorize');
$compile(el)($scope);
});
}
};
}]);
要素
私のアプリケーションではタブに大きく依存しています。適切なクレームを持たないユーザーから非表示にしたいタブに認可ディレクティブを適用します。
<tabset>
<tab ng-cloak heading="Users" authorize="{{allowUserManagement}}">
...html content
</tab>
</tabset>
ルート
ui-routerを使用しています。残念ながら、そうでない人のために、既成のAngularJSルーターのコードはありません。
$stateChangeStartでルートを認証します。これはそのイベント内のコードです。
$rootScope.$on("$stateChangeStart", function(event, toState, toParams, fromState, fromParams){
AuthenticationManager.authenticate(event, toState, toParams);
});
ルートを認可する関数。認可されている場合、ルートは続行することが許可されます。認可されていない場合、メッセージがユーザーに表示され、ホームページにリダイレクトされます。
function authorizedRoute(toState, location, toaster, breadCrumbs){
if(toState.authorization !== undefined){
AuthorizationContext.authorized(toState.authorization, function(authorized){
if(!authorized){
toaster.pop('error', 'Error', 'You are not authorized to view this page.');
location.path("/search");
} else {
breadCrumbs();
}
});
} else{
breadCrumbs();
}
}
このルーター定義では、「authorization」というプロパティに気付くでしょう。ユーザーがこのクレームを持っている場合、進行することが許可されます。
angular.module('agency',
[
'ui.router',
'services'
])
.config(function config($stateProvider){
$stateProvider.state( 'agency', {
url: '/agency',
controller: 'agency.index',
templateUrl: 'agency/agency.tpl.html',
authenticate: true,
authorization:'401d91e7-6ea0-46b4-9a10-530e3483ce15',
data:{ pageTitle: 'Agency' }
});
});
データ
場合によっては、サーバーにデータをリクエストしたくない場合があります。ユーザーがクレームを持っている場合、リクエストを行うことが許可されます。
記事の最初の上記のAuthorizationContextはauthoriedExecutionのコードを示しています。ここでその使用法を見ます。
AuthorizationContext.authorizedExecution(Keys.authorization.allowUserManagement, function(){
//execute code, if the loggedin user has rights.
});
上記で述べたように、これはサーバーの保護の代わりにはなりません。このコードは素晴らしいユーザーエクスペリエンスを提供するために機能します。
Author: Chuck Conway is an AI Engineer with nearly 30 years of software engineering experience. He builds practical AI systems—content pipelines, infrastructure agents, and tools that solve real problems—and shares what he’s learning along the way. Connect with him on social media: X (@chuckconway) or visit him on YouTube and on SubStack.
著者: Chuck Conwayは、ソフトウェアエンジニアリングの経験が30年近くあるAIエンジニアです。彼は実用的なAIシステム(コンテンツパイプライン、インフラストラクチャエージェント、実際の問題を解決するツール)を構築し、学んだことを共有しています。ソーシャルメディアで彼とつながってください: X (@chuckconway) または YouTube と SubStack で彼を訪問してください。