[解決済み】AngularJS ui-router ログイン認証
質問
私はAngularJSの初心者ですが、以下のシナリオでangular-"ui-router"をどのように使用すればよいのか、少し混乱しています。
私は、2つのセクションからなるウェブアプリケーションを構築しています。最初のセクションは、ログインとサインアップのビューを持つホームページで、2番目のセクションはダッシュボード(ログイン成功後)です。
を作成しました。
index.html
をアンギュラーアプリのホームセクションに、そして
ui-router
を処理するためのコンフィグ
/login
と
/signup
のビューを表示します。
というファイルがあり、もう1つのファイル
dashboard.html
は、ダッシュボードセクションとそのアプリ、そして
ui-router
コンフィグで多くのサブビューを扱えるようにします。
今、私はダッシュボードセクションを終え、2つのセクションとそれぞれの異なるangularアプリをどのように組み合わせたらよいのかわかりません。ホームアプリからダッシュボードアプリにリダイレクトさせるにはどうしたらよいでしょうか?
解決方法は?
現在、より良いデモを作るとともに、これらのサービスのいくつかを使えるモジュールに整理している最中ですが、こんな感じです。これは、いくつかの注意点を回避するための複雑なプロセスなので、がんばってください。これをいくつかのパーツに分解する必要があります。
まず、ユーザーのIDを保存するためのサービスが必要です。私はこれを
principal
. これは、ユーザーがログインしているかどうかをチェックし、リクエストに応じて、ユーザーの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');
}
}
});
}
};
}
])
これで、あとは
ui-router
's
$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 を追跡する際に厄介なのは、ユーザーがすでに認証済みである場合 (たとえば、以前のセッションの後でページを訪れ、認証トークンをクッキーに保存した場合、あるいはページをハードリフレッシュした場合、あるいはリンクから URL に移動した場合) にそれを確認することです。というのも
ui-router
が動作するため、認証チェックの前に一度 ID の解決を行う必要があります。これを行うには
resolve
オプションを使用します。私は、すべてのステートが継承するサイトの親ステートを1つ持っており、これは、他の何かが起こる前にプリンシパルを解決することを強制します。
$stateProvider.state('site', {
'abstract': true,
resolve: {
authorize: ['authorization',
function(authorization) {
return authorization.authorize();
}
]
},
template: '<div ui-view />'
})
ここでもうひとつ問題が...。
resolve
は一度だけ呼ばれます。ID検索用のプロミスが完了すると、resolveデリゲートは二度と実行されません。つまり、認証のチェックは二箇所で行わなければなりません。
resolve
これはアプリの最初のロードをカバーし、もう1つは
$stateChangeStart
が解決された場合、つまりステートをナビゲートするすべての時間をカバーします。
さて、ここまでで何ができたでしょうか?
- アプリのロード時に、ユーザーがログインしているかどうかを確認します。
- ログインしているユーザーに関する情報を追跡します。
- ログインが必要な状態については、サインイン状態にリダイレクトしています。
- アクセスするための認証がない場合は、アクセス拒否の状態にリダイレクトしています。
- ログインが必要な場合、ユーザーが要求した元の状態にリダイレクトする仕組みがあります。
- ユーザーをサインアウトさせることができます(認証チケットを管理するクライアントまたはサーバーのコードと協調して配線する必要があります)。
- 私たちは しない は、ユーザーがブラウザを再読み込みしたり、リンクをドロップしたりするたびに、サインインページに戻る必要があります。
ここから先はどうすればいいのか?さて、サインインを必要とする地域に州を組織化することができます。認証済み/認可済みのユーザーを要求するには、以下のように
data
と共に
roles
をこれらの状態 (継承を使用する場合は、その親) に追加します。ここでは、リソースをAdminsに制限しています。
.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
ロールのメンバーです。これまで説明してきたすべての権限設定は、そこから派生していくでしょう。
関連
-
[解決済み】フォームコントロールの値アクセサがない
-
[解決済み】BootstrapのCollapseが折りたたまれない
-
[解決済み] Uncaught (in promise) TypeError: フェッチに失敗してCorsエラー
-
[解決済み] AngularJSでデータバインディングはどのように機能するのですか?
-
[解決済み] AngularJSを使用して、ブラウザのコンソールで$scope変数にアクセスするにはどうすればよいですか?
-
[解決済み] AngularJSで$scope.$watchと$scope.$applyを使用するにはどうすればよいですか?
-
[解決済み] angular-routeとangular-ui-routerの違いは何ですか?
-
[解決済み] AngularJS コントローラにおける 'this' と $scope の比較
-
[解決済み] 部分テンプレートとテンプレートの複雑なネスト
-
[解決済み】react router v4 / v5でネストされたルート
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】 Uncaught TypeError: data.push is not a function
-
[解決済み】フォームコントロールの値アクセサがない
-
[解決済み】Uncaught ReferenceError: angular is not defined - AngularJSが動作しない。
-
[解決済み】Uncaught TypeError: nullのプロパティ'value'を読み取ることができない
-
[解決済み】JavaScriptのinnerHTMLで要素が更新されない
-
[解決済み】別のjsファイル内でJavaScriptの関数を呼び出す
-
[解決済み】エラー:リスン EACCES 0.0.0.0:80 OSx Node.js
-
[解決済み】Uncaught SyntaxError: JSON の位置 0 に予期しないトークン u があります。
-
[解決済み】Redux TypeError: 未定義のプロパティ 'apply' を読み取れない
-
[解決済み】Uncaught ReferenceError。Firebase は定義されていません。