我正在制作一个更漂亮的演示,并将这些服务清理成可使用的模块,但这是一个复杂的过程需要解决一些限制条件,请耐心等待。你需要将其分解成几个部分。
查看这个 plunk。
首先,你需要一个用于存储用户身份的服务。我称它为 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;
if (angular.isDefined(_identity)) {
deferred.resolve(_identity);
return deferred.promise;
}
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) {
$state.go('accessdenied');
} else {
$rootScope.returnToState
= $rootScope.toState;
$rootScope.returnToStateParams
= $rootScope.toStateParams;
$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)
{
$rootScope.toState = toState;
$rootScope.toStateParams = toStateParams;
if (principal.isIdentityResolved())
authorization.authorize();
});
}
]);
在追踪用户身份时,棘手的部分是如果已经进行了身份验证(例如,在上一个会话后访问页面并在cookie中保存了授权令牌,或者您刷新了页面或从链接跳转到了URL),则需要查找身份。由于ui-router
的工作方式,您需要在进行身份验证之前先进行身份解析。您可以使用状态配置中的resolve
选项来完成此操作。我为站点设置了一个父状态,所有状态都继承自该状态,这强制解析主体先于其他任何事件发生。
$stateProvider.state('site', {
'abstract': true,
resolve: {
authorize: ['authorization',
function(authorization) {
return authorization.authorize();
}
]
},
template: '<div ui-view />'
})
这里还有一个问题... resolve
只被调用了一次。一旦您的身份查找承诺完成,它将不会再运行 resolve 委托。因此,我们必须在两个地方执行身份验证检查:一次是在resolve
中的身份识别承诺解决期间,这涵盖了应用程序加载时的第一次,另一次是在 $stateChangeStart
中,如果已经解析,那么覆盖了任何时间您在状态之间导航。
好的,到目前为止我们做了什么?
- 检查应用程序在加载时用户是否已登录。
- 跟踪已登录用户的信息。
- 重定向到需要用户已登录的状态的登录状态。
- 如果没有权限访问,则将其重定向到访问被拒绝的状态。
- 如果需要他们登录,我们有一种机制可以将用户重定向回他们请求的原始状态。
- 我们可以注销用户(需要与管理您的授权票证的任何客户端或服务器代码一起使用)。
- 我们不需要每次重新加载浏览器或单击链接时都将用户发送回登录页面。
从这里开始怎么办呢?好吧,您可以将状态组织成需要登录的区域。通过向这些状态(或其父级,如果要使用继承)添加具有角色
的 data
,可以要求经过身份验证/授权的用户。在这里,我们将资源限制为管理员:
.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>
等等等等,总之,在您的示例应用程序中,您将拥有一个主页状态,该状态将允许未经身份验证的用户访问。他们可以链接到登录或注册状态,或者将这些表单构建到该页面中。无论什么适合您。
仪表板页面可以从要求用户已登录且为“用户”角色成员的状态继承。我们讨论过的所有授权内容都将从那里流向。