私はより良いデモを作成し、これらのサービスのいくつかを使用可能なモジュールにクリーンアップする過程にありますが、ここに私が思いついたものがあります。これは、いくつかの注意事項を回避するための複雑なプロセスであるため、ここでしばらくお待ちください。これをいくつかに分割する必要があります。
このプランクをご覧ください。
まず、ユーザーのIDを保存するサービスが必要です。これを呼ぶprincipal
。これは、ユーザーがログインしているかどうかを確認するためにチェックでき、要求に応じて、ユーザーのアイデンティティに関する重要な情報を表すオブジェクトを解決できます。これは何でもかまいませんが、基本的には表示名、ユーザー名、場合によってはメール、ユーザーが所属するロール(これがアプリに該当する場合)です。プリンシパルには、ロールチェックを実行するメソッドもあります。
.factory('principal', ['$q', '$http', '$timeout',
function($q, $http, $timeout) {
var _identity = undefined,
_authenticated = false;
return {
isIdentityResolved: function() {
return angular.isDefined(_identity);
},
isAuthenticated: function() {
return _authenticated;
},
isInRole: function(role) {
if (!_authenticated || !_identity.roles) return false;
return _identity.roles.indexOf(role) != -1;
},
isInAnyRole: function(roles) {
if (!_authenticated || !_identity.roles) return false;
for (var i = 0; i < roles.length; i++) {
if (this.isInRole(roles[i])) return true;
}
return false;
},
authenticate: function(identity) {
_identity = identity;
_authenticated = identity != null;
},
identity: function(force) {
var deferred = $q.defer();
if (force === true) _identity = undefined;
// check and see if we have retrieved the
// identity data from the server. if we have,
// reuse it by immediately resolving
if (angular.isDefined(_identity)) {
deferred.resolve(_identity);
return deferred.promise;
}
// otherwise, retrieve the identity data from the
// server, update the identity object, and then
// resolve.
// $http.get('/svc/account/identity',
// { ignoreErrors: true })
// .success(function(data) {
// _identity = data;
// _authenticated = true;
// deferred.resolve(_identity);
// })
// .error(function () {
// _identity = null;
// _authenticated = false;
// deferred.resolve(_identity);
// });
// for the sake of the demo, fake the lookup
// by using a timeout to create a valid
// fake identity. in reality, you'll want
// something more like the $http request
// commented out above. in this example, we fake
// looking up to find the user is
// not logged in
var self = this;
$timeout(function() {
self.authenticate(null);
deferred.resolve(_identity);
}, 1000);
return deferred.promise;
}
};
}
])
次に、ユーザーが行きたい状態をチェックし、ユーザーがログインしていることを確認し(必要な場合。サインインやパスワードのリセットなどには不要)、次にロールチェックを行う(アプリの場合)サービスが必要です。これが必要です)。認証されていない場合は、サインインページに送信します。認証されているが役割チェックに失敗した場合は、アクセス拒否ページに送信します。このサービスを呼びますauthorization
。
.factory('authorization', ['$rootScope', '$state', 'principal',
function($rootScope, $state, principal) {
return {
authorize: function() {
return principal.identity()
.then(function() {
var isAuthenticated = principal.isAuthenticated();
if ($rootScope.toState.data.roles
&& $rootScope.toState
.data.roles.length > 0
&& !principal.isInAnyRole(
$rootScope.toState.data.roles))
{
if (isAuthenticated) {
// user is signed in but not
// authorized for desired state
$state.go('accessdenied');
} else {
// user is not authenticated. Stow
// the state they wanted before you
// send them to the sign-in state, so
// you can return them when you're done
$rootScope.returnToState
= $rootScope.toState;
$rootScope.returnToStateParams
= $rootScope.toStateParams;
// now, send them to the signin state
// so they can log in
$state.go('signin');
}
}
});
}
};
}
])
今、すべてのあなたは、ISが上で聞く行う必要があるui-router
の$stateChangeStart
。これにより、現在の状態と彼らが行きたい状態を調べ、承認チェックを挿入する機会が与えられます。失敗した場合は、ルートの遷移をキャンセルするか、別のルートに変更できます。
.run(['$rootScope', '$state', '$stateParams',
'authorization', 'principal',
function($rootScope, $state, $stateParams,
authorization, principal)
{
$rootScope.$on('$stateChangeStart',
function(event, toState, toStateParams)
{
// track the state the user wants to go to;
// authorization service needs this
$rootScope.toState = toState;
$rootScope.toStateParams = toStateParams;
// if the principal is resolved, do an
// authorization check immediately. otherwise,
// it'll be done when the state it resolved.
if (principal.isIdentityResolved())
authorization.authorize();
});
}
]);
ユーザーのIDの追跡に関する注意が必要な部分は、既に認証されている場合(たとえば、前のセッションの後にページにアクセスし、認証トークンをCookieに保存している場合、またはページをハードリフレッシュした場合)です。リンクからURLにドロップします)。ui-router
動作する方法のため、認証チェックの前に、ID解決を1回実行する必要があります。resolve
状態設定のオプションを使用してこれを行うことができます。私は、すべての州が継承するサイトの親州を1つ持っています。
$stateProvider.state('site', {
'abstract': true,
resolve: {
authorize: ['authorization',
function(authorization) {
return authorization.authorize();
}
]
},
template: '<div ui-view />'
})
ここに別の問題があります... resolve
一度だけ呼び出されます。IDルックアップのプロミスが完了すると、解決デリゲートは再度実行されません。したがって、私たちは2つの場所で認証チェックを行う必要があります。1つはでのIDプロミスの解決に従ってresolve
、アプリが初めてロード$stateChangeStart
されたときをカバーし、もう1つは解決が行われた場合は状態をナビゲートするときをカバーします。
では、これまでに何をしましたか?
- ユーザーがログインしている場合、アプリがいつ読み込まれるかを確認します。
- ログインしたユーザーに関する情報を追跡します。
- ユーザーのログインが必要な状態の場合は、サインイン状態にリダイレクトします。
- アクセス権限がない場合は、アクセス拒否状態にリダイレクトします。
- ログインが必要な場合、ユーザーをリクエストした元の状態にリダイレクトするメカニズムがあります。
- ユーザーをサインアウトできます(認証チケットを管理するクライアントまたはサーバーのコードと連携して接続する必要があります)。
- 私たちはしていないサインインページに戻って、彼らは彼らのブラウザをリロードするか、リンクの上にドロップするたびにユーザーを送信する必要があります。
ここからどこにいきますか?サインインが必要なリージョンに状態を編成できます。認証済み/承認済みのユーザーにこれらの状態(または継承を使用する場合はその親)にを追加することdata
でroles
、それらを要求できます。ここでは、リソースを管理者に制限しています。
.state('restricted', {
parent: 'site',
url: '/restricted',
data: {
roles: ['Admin']
},
views: {
'content@': {
templateUrl: 'restricted.html'
}
}
})
これで、ユーザーがルートにアクセスできるものを州ごとに制御できます。他に何か心配?ログインしているかどうかに基づいて、ビューの一部のみを変更するのでしょうか?問題ない。principal.isAuthenticated()
またはを使用するprincipal.isInRole()
条件付きでテンプレートまたは要素を表示できる多数の方法のいずれかをます。
まず、principal
コントローラーなどに注入し、スコープに貼り付けて、ビューで簡単に使用できるようにします。
.scope('HomeCtrl', ['$scope', 'principal',
function($scope, principal)
{
$scope.principal = principal;
});
要素を表示または非表示にする:
<div ng-show="principal.isAuthenticated()">
I'm logged in
</div>
<div ng-hide="principal.isAuthenticated()">
I'm not logged in
</div>
等々。とにかく、あなたのサンプルアプリでは、認証されていないユーザーが立ち寄ることができるホームページの状態があります。彼らはサインインまたはサインアップ状態へのリンクを持っているか、それらのフォームをそのページに組み込むことができます。あなたに合うものは何でも。
ダッシュボードページはすべて、ユーザーがログインしている、たとえばUser
ロールメンバーである必要がある状態から継承できます。ここで説明したすべての承認はそこから流れます。