我建立一个由两个部分组成的Web应用程序。第一部分是具有登录和注册视图的主页,第二部分是仪表板(成功登录后)。
我已经创建了一个index.html的主页部分,其角度应用程序和ui路由器配置来处理/ login和/ signup视图,
还有另一个文件dashboard.html用于仪表板部分及其应用程序和ui-router配置以处理许多子视图。
现在我完成了仪表板部分,不知道如何将这两个部分与他们不同的角度应用程序。如何让家庭应用重定向到信息中心应用?
首先,您需要一个服务来存储用户的身份。我称之为校长。可以检查用户是否登录,并且根据请求,它可以解析表示关于用户身份的基本信息的对象。这可以是任何你需要的,但必要的将是显示名称,用户名,可能是电子邮件,和用户所属的角色(如果这适用于你的应用程序)。校长还有方法来做角色检查。
.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; } }; } ])
其次,您需要一个检查用户想要访问的状态的服务,确保他们已登录(如果需要,无需登录,密码重置等),然后进行角色检查(如果您的应用程序需要这个)。如果它们未通过身份验证,请将其发送到登录页面。如果它们已通过身份验证,但未能通过角色检查,请将其发送到访问被拒绝页面。我叫这个服务授权。
.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路由器的$stateChangeStart
.这让你有机会检查当前状态,他们想去的状态,并插入您的授权检查。如果失败,您可以取消路由转换,或更改到不同的路由。
.run(['$rootScope','$stateParams','authorization',$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路由器的工作方式,您需要在您的身份验证检查之前进行一次身份验证。你可以使用状态配置中的resolve选项。我有一个父状态为所有状态继承的网站,这迫使委托人在任何事情发生之前解决。
$stateProvider.state('site',{ 'abstract': true,resolve: { authorize: ['authorization',function(authorization) { return authorization.authorize(); } ] },template: '<div ui-view />' })
这里有另一个问题…解决只被调用一次。一旦您的身份查询的承诺完成,它将不再运行解决委托。所以我们必须在两个地方进行你的验证检查:一次根据你的身份承诺解析,这覆盖了你的应用程序第一次加载,一次在$ stateChangeStart如果分辨率已经完成,这涵盖任何时候你导航状态。
好吧,到目前为止我们做了什么?
>我们会检查用户登录时应用加载的时间。
>我们跟踪登录用户的信息。
>我们将其重定向到需要用户登录的状态的登录状态。
>如果他们没有访问权限,我们将它们重定向到访问被拒绝状态。
>我们有一个机制,将用户重定向到他们请求的原始状态,如果我们需要他们登录。
>我们可以签出用户(需要与管理您的身份验证票据的任何客户端或服务器代码一起连接)。
>每当用户重新加载浏览器或关闭链接时,我们不需要将用户重新发送到登录页面。
我们从这里去哪里?那么,您可以将您的状态组织到需要登录的区域中。通过将角色的数据添加到这些状态(或者如果您要使用继承,则可以是这些状态的父级)来请求认证/授权用户。在这里,我们将资源限制为Admins:
.state('restricted',{ parent: 'site',url: '/restricted',data: { roles: ['Admin'] },views: { 'content@': { templateUrl: 'restricted.html' } } })
现在你可以控制状态,用户可以访问一个路由。任何其他问题?也许只根据视图是否登录才改变视图的一部分?没问题。使用principal.isAuthenticated()或甚至principal.isInRole()与您有条件地显示模板或元素的许多方法中的任何一种。
首先,将principal注入控制器或其他任何东西,并将其粘贴到范围,以便您可以轻松地在您的视图中使用它:
.scope('HomeCtrl',['$scope',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>
等等,等等,等等。无论如何,在您的示例应用程序中,您将有一个主页的状态,将允许未经身份验证的用户删除。它们可以包含指向登录或注册状态的链接,或者将那些表单内置到该页面中。无论适合你。