我是 AngularJS 的新手,我对如何在以下场景中使用 angular-“ui-router” 感到有些困惑:
我正在构建一个由两个部分组成的 Web 应用程序。第一部分是主页及其登录和注册视图,第二部分是仪表板(成功登录后)。
我index.html为主页部分创建了一个带有角度应用程序和ui- router配置的文件来处理/login和/signup查看,仪表板部分还有另一个文件dashboard.html及其应用程序和ui- router配置来处理许多子视图。
index.html
ui- router
/login
/signup
dashboard.html
现在我完成了仪表板部分,不知道如何将这两个部分与它们不同的角度应用程序结合起来。我怎么能告诉主页应用程序重定向到仪表板应用程序?
我正在制作一个更好的演示,并将其中一些服务清理成一个可用的模块,但这就是我想出的。这是一个解决一些警告的复杂过程,所以请坚持下去。你需要把它分解成几个部分。
看看这个笨蛋。
首先,您需要一个服务来存储用户的身份。我称之为principal。可以检查用户是否登录,并且根据请求,它可以解析代表有关用户身份的基本信息的对象。这可以是您需要的任何内容,但基本要素是显示名称、用户名、可能是电子邮件以及用户所属的角色(如果这适用于您的应用程序)。Principal 也有进行角色检查的方法。
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。
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. 这使您有机会检查当前状态、他们想要进入的状态,并插入您的授权检查。如果失败,您可以取消路由转换,或更改为不同的路由。
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(); }); } ]);
跟踪用户身份的棘手部分是在您已经通过身份验证时查找它(例如,您在前一个会话之后访问该页面,并将身份验证令牌保存在 cookie 中,或者您可能很难刷新页面,或者从链接拖放到 URL 上)。由于这种方式ui- router有效,您需要在身份验证检查之前进行一次身份解析。您可以使用resolve状态配置中的选项来执行此操作。我有一个所有状态都继承自的站点的父状态,这会强制在其他任何事情发生之前解决主体。
resolve
$stateProvider.state('site', { 'abstract': true, resolve: { authorize: ['authorization', function(authorization) { return authorization.authorize(); } ] }, template: '<div ui-view />' })
这里还有另一个问题......resolve只被调用一次。一旦您对身份查找的承诺完成,它就不会再次运行解析委托。因此,我们必须在两个地方进行身份验证检查:一次根据您的身份承诺解决resolve,包括您的应用程序第一次加载,以及一次在$stateChangeStart解决方案已经完成的情况下,包括您在状态中导航的任何时间。
好的,那么到目前为止我们做了什么?
我们从这里去哪里?好吧,您可以将您的状态组织成需要登录的区域。您可以通过将datawith添加roles到这些状态(或它们的父级,如果您想使用继承)来要求经过身份验证/授权的用户。在这里,我们将资源限制为管理员:
data
roles
.state('restricted', { parent: 'site', url: '/restricted', data: { roles: ['Admin'] }, views: { 'content@': { templateUrl: 'restricted.html' } } })
现在,您可以逐个状态地控制用户可以访问路由的内容。还有其他顾虑吗?也许根据他们是否登录只改变视图的一部分?没问题。使用principal.isAuthenticated()或什principal.isInRole()至可以有条件地显示模板或元素的多种方式中的任何一种。
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角色成员的状态。我们讨论过的所有授权内容都将从那里流出。
User