AngularJS ui-router ログイン認証 質問する

AngularJS ui-router ログイン認証 質問する

私は AngularJS を初めて使用するので、次のシナリオで angular-"ui-router" をどのように使用すればよいのか少し混乱しています。

私は 2 つのセクションで構成される Web アプリケーションを構築しています。最初のセクションは、ログインとサインアップのビューがあるホームページで、2 番目のセクションはダッシュボード (ログイン成功後) です。

私は、ビューを処理するための Angular アプリと構成をindex.html含むホーム セクション用のファイルを作成しました。また、多くのサブビューを処理するためのアプリと構成を含むダッシュボード セクション用の別のファイルもあります。ui-router/login/signupdashboard.htmlui-router

ダッシュボード セクションは完了しましたが、異なる Angular アプリを持つ 2 つのセクションを組み合わせる方法がわかりません。ホーム アプリにダッシュボード アプリにリダイレクトするように指示するにはどうすればよいですか?

ベストアンサー1

私は現在、より優れたデモを作成し、これらのサービスの一部を使用可能なモジュールに整理しているところですが、ここに私が思いついたものを示します。これは、いくつかの注意事項を回避するための複雑なプロセスですので、しばらくお待ちください。これをいくつかの部分に分割する必要があります。

このプランクを見てください

まず、ユーザーの 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です$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 />'
})

ここで別の問題があります... はresolve1 回しか呼び出されません。アイデンティティ検索のプロミスが完了すると、解決デリゲートは再度実行されません。そのため、認証チェックを 2 か所で実行する必要があります。1 回目は で解決するアイデンティティ プロミスに従ってresolve、アプリが初めてロードされるときをカバーし、もう 1 回目は$stateChangeStart解決が完了した場合に、状態間を移動するたびにカバーします。

さて、これまで何をしてきましたか?

  1. ユーザーがログインしているかどうか、アプリが読み込まれるタイミングを確認します。
  2. ログインしたユーザーに関する情報を追跡します。
  3. ユーザーのログインが必要な状態の場合は、サインイン状態にリダイレクトします。
  4. アクセス権限がない場合は、アクセス拒否状態にリダイレクトします。
  5. ログインが必要な場合、ユーザーを要求された元の状態にリダイレクトするメカニズムがあります。
  6. ユーザーをサインアウトできます (認証チケットを管理するクライアントまたはサーバー コードと連携して接続する必要があります)。
  7. ユーザーがブラウザを再読み込みしたりリンクをクリックするたびに、サインイン ページに戻る必要はありません

ここから先はどうなるでしょうか? サインインが必要なリージョンに状態を整理することができます。これらの状態 (継承を使用する場合は、それらの親) に を追加することで、認証済み/承認済みのユーザーを要求することができますdatarolesここでは、リソースを管理者に制限します。

.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ロール メンバーであることを要求する状態から継承できます。これまでに説明したすべての承認処理はそこから実行されます。

おすすめ記事