AngularJS UI路由器登录身份验证 [英] AngularJS ui-router login authentication

查看:76
本文介绍了AngularJS UI路由器登录身份验证的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是AngularJS的新手,在以下情况下我对如何使用angular-"ui-router"感到有些困惑:

I am new to AngularJS, and I am a little confused of how I can use angular-"ui-router" in the following scenario:

我正在构建一个包含两个部分的Web应用程序.第一部分是带有登录和注册视图的主页,第二部分是仪表板(成功登录后).

I am building a web application which consists of two sections. The first section is the homepage with its login and signup views, and the second section is the dashboard (after successful login).

我已经用它的角度应用程序和ui-router配置为home部分创建了index.html,以处理/login/signup视图, 仪表板部分的另一个文件dashboard.html及其应用程序和ui-router配置可处理许多子视图.

I have created an index.html for the home section with its angular app and ui-router config to handle /login and /signup views, and there is another file dashboard.html for the dashboard section with its app and ui-router config to handle many sub views.

现在我完成了仪表板部分,并且不知道如何将这两个部分与不同的角度应用程序结合起来.我如何告诉家用应用程序重定向到仪表板应用程序?

Now I finished the dashboard section and don't know how to combine the two sections with their different angular apps. How could I tell the home app to redirect to the dashboard app?

推荐答案

我正在制作一个更好的演示,并将其中的一些服务清理到一个可用的模块中,但这就是我要提出的内容和.要解决一些警告,这是一个复杂的过程,所以请继续.您需要将其分解为几部分.

I'm in the process of making a nicer demo as well as cleaning up some of these services into a usable module, but here's what I've come up with. This is a complex process to work around some caveats, so hang in there. You'll need to break this down into several pieces.

看看这个小问题.

首先,您需要一项服务来存储用户的身份.我将此称为principal.可以检查该用户是否登录,并且可以根据请求解析一个代表有关用户身份的基本信息的对象.这可以是您需要的任何内容,但必不可少的是显示名称,用户名(可能是电子邮件)以及用户所属的角色(如果这适用于您的应用程序). Principal还具有进行角色检查的方法.

First, you need a service to store the user's identity. I call this principal. It can be checked to see if the user is logged in, and upon request, it can resolve an object that represents the essential information about the user's identity. This can be whatever you need, but the essentials would be a display name, a username, possibly an email, and the roles a user belongs to (if this applies to your app). Principal also has methods to do role checks.

.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.

Second, you need a service that checks the state the user wants to go to, makes sure they're logged in (if necessary; not necessary for signin, password reset, etc.), and then does a role check (if your app needs this). If they are not authenticated, send them to the sign-in page. If they are authenticated, but fail a role check, send them to an access denied page. I call this service 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上进行监听.这使您有机会检查当前状态,他们想进入的状态,并插入您的授权检查.如果失败,则可以取消路由转换,或更改为其他路由.

Now all you need to do is listen in on ui-router's $stateChangeStart. This gives you a chance to examine the current state, the state they want to go to, and insert your authorization check. If it fails, you can cancel the route transition, or change to a different route.

.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();
      });
    }
  ]);

关于跟踪用户身份的棘手部分是查找您是否已通过身份验证(例如,您在上一个会话后访问该页面,并将auth令牌保存在cookie中,或者您可能难以刷新)页,或从链接拖放到URL上).由于ui-router的工作方式,您需要在身份验证之前进行一次身份解析.您可以使用状态配置中的resolve选项来执行此操作.对于所有状态都继承自该站点的站点,我有一个父状态,这将迫使主体在发生其他任何事情之前先进行解析.

The tricky part about tracking a user's identity is looking it up if you've already authenticated (say, you're visiting the page after a previous session, and saved an auth token in a cookie, or maybe you hard refreshed a page, or dropped onto a URL from a link). Because of the way ui-router works, you need to do your identity resolve once, before your auth checks. You can do this using the resolve option in your state config. I have one parent state for the site that all states inherit from, which forces the principal to be resolved before anything else happens.

$stateProvider.state('site', {
  'abstract': true,
  resolve: {
    authorize: ['authorization',
      function(authorization) {
        return authorization.authorize();
      }
    ]
  },
  template: '<div ui-view />'
})

这里还有另一个问题... resolve仅被调用一次.一旦您完成对身份查询的承诺,就不会再运行解析委托.因此,我们必须在两个地方进行身份验证:一次是根据您的身份承诺在resolve中进行解析,这涵盖了您的应用首次加载;一次是在$stateChangeStart中,如果解决方案已完成,则涵盖了任何时间您可以在各州之间导航.

There's another problem here... resolve only gets called once. Once your promise for identity lookup completes, it won't run the resolve delegate again. So we have to do your auth checks in two places: once pursuant to your identity promise resolving in resolve, which covers the first time your app loads, and once in $stateChangeStart if the resolution has been done, which covers any time you navigate around states.

好,那么到目前为止我们做了什么?

OK, so what have we done so far?

  1. 如果用户已登录,我们会检查该应用何时加载.
  2. 我们跟踪有关已登录用户的信息.
  3. 我们将其重定向到需要用户登录的状态的登录状态.
  4. 如果他们无权访问它们,我们会将其重定向到拒绝访问状态.
  5. 如果需要用户登录,我们有一种机制可以将用户重定向回他们请求的原始状态.
  6. 我们可以注销用户(需要与管理您的身份验证票的任何客户端或服务器代码保持一致)
  7. 我们不需要在用户每次重新加载浏览器或断开链接时都需要将他们带回到登录页面.
  1. We check to see when the app loads if the user is logged in.
  2. We track info about the logged in user.
  3. We redirect them to sign in state for states that require the user to be logged in.
  4. We redirect them to an access denied state if they do not have authorization to access it.
  5. We have a mechanism to redirect users back to the original state they requested, if we needed them to log in.
  6. We can sign a user out (needs to be wired up in concert with any client or server code that manages your auth ticket).
  7. We don't need to send users back to the sign-in page every time they reload their browser or drop on a link.

我们从这里去哪里?好了,您可以将状态组织到需要登录的区域.可以通过将dataroles添加到这些状态(如果要使用继承,则可以是它们的父级)来要求经过身份验证/授权的用户.在这里,我们将资源限制为管理员:

Where do we go from here? Well, you can organize your states into regions that require sign in. You can require authenticated/authorized users by adding data with roles to these states (or a parent of them, if you want to use inheritance). Here, we restrict a resource to Admins:

.state('restricted', {
    parent: 'site',
    url: '/restricted',
    data: {
      roles: ['Admin']
    },
    views: {
      'content@': {
        templateUrl: 'restricted.html'
      }
    }
  })

现在,您可以按状态控制哪些用户可以访问路由.还有其他问题吗?也许仅根据视图是否已登录来更改视图的一部分?没问题.将principal.isAuthenticated()或什至principal.isInRole()与您可以有条件地显示模板或元素的多种方式一起使用.

Now you can control state-by-state what users can access a route. Any other concerns? Maybe varying only part of a view based on whether or not they are logged in? No problem. Use the principal.isAuthenticated() or even principal.isInRole() with any of the numerous ways you can conditionally display a template or an element.

首先,将principal注入控制器或其他任何东西,并将其粘贴到示波器上,以便可以在视图中轻松使用它:

First, inject principal into a controller or whatever, and stick it to the scope so you can use it easily in your view:

.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>

等等,依此类推.无论如何,在示例应用程序中,您将拥有主页状态,该状态将使未经身份验证的用户掉队.他们可以具有指向登录或注册状态的链接,或者可以将那些表单内置到该页面中.随便什么.

Etc., so on, so forth. Anyways, in your example app, you would have a state for home page that would let unauthenticated users drop by. They could have links to the sign-in or sign-up states, or have those forms built into that page. Whatever suits you.

所有仪表板页面都可以继承一种状态,该状态要求用户登录并成为User角色成员.我们讨论的所有授权内容都将从那里流传.

The dashboard pages could all inherit from a state that requires the users to be logged in and, say, be a User role member. All the authorization stuff we've discussed would flow from there.

这篇关于AngularJS UI路由器登录身份验证的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆