本文介绍在AngularJS单页面应用中实现基于角色的访问控制(RBAC)的方法。通过Service单例共享用户状态,结合路由配置与$rootScope监听routeChangeStart事件,实现UI显示控制、路由拦截及HTTP状态码处理,确保页面访问安全。
基于角色的访问控制(RBAC)原理
权限设计中常见的方案是RBAC(基于角色的访问控制)。其核心逻辑是将权限授予角色而非直接授予具体用户,在用户与权限之间建立角色关联,每种角色对应特定的权限集合。
用户被分配角色后即拥有该角色的所有操作权限。这种方式避免了每次创建用户时重复分配权限的操作,只需分配角色即可。由于角色权限变更频率远低于用户权限变更,这简化了权限管理并降低了系统开销。
AngularJS前端权限处理环节
在AngularJS构建的单页面应用中实现上述架构,前端工程师需要在项目中处理三个关键环节。
首先是UI层面的处理,根据用户拥有的权限决定页面中特定内容的显示或隐藏。
其次是路由层面的处理,当用户尝试访问无权限的URL时,系统应自动跳转至错误提示页面。
最后是HTTP请求层面的处理,当数据请求返回的状态码为401或403时,通常需要将用户重定向至错误提示页面。
使用Service实现身份验证
若在客户端使用AngularJS进行身份验证,推荐使用Service来实现。因为Service是单例模式,可以方便地在所有View、Controller、Directive、Filter及其他Service中共享数据,无需暴露全局变量,同时保证了良好的封装性。
以下是一个简单的实现示例:
services.factory('UserService', [function() {
var sdo = {
isLogged: false,
username: ''
};
return sdo;
}]);
在AngularJS中,使用Service通常通过依赖注入声明来实现,例如:
var controllers = angular.module('myApp.controllers', []);
/* ... */
controllers.controller('loginController', ['$scope', '$http', 'UserService', function(scope, $http, User) {
}]);
在loginController中,可以定义一个login函数来向服务器验证用户身份:
scope.login = function() {
var config = { /* ... */ } // configuration object
$http(config)
.success(function(data, status, headers, config) {
if (data.status) {
// succefull login
User.isLogged = true;
User.username = data.username;
}
else {
User.isLogged = false;
User.username = '';
}
})
.error(function(data, status, headers, config) {
User.isLogged = false;
User.username = '';
});
}
此后,任何声明依赖了UserService的Controller、View或Filter,都可以通过UserService.isLogged属性来判断用户是否已通过验证,或者是否为匿名用户。
路由控制与身份检查
由于AngularJS通常使用模板将页面拆分重组,此时需利用routeProvider来控制各页面的访问规则:
app.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/login', { templateUrl: 'partials/login.html', controller: 'loginCtrl' , access: {isFree: true}});
$routeProvider.when('/main', { templateUrl: 'partials/main.html', controller: 'mainCtrl' });
$routeProvider.otherwise({ redirectTo: '/main' });
}]);
部分页面无需身份验证即可访问,例如login.html;而另一些页面仅允许登录用户查看,例如main.html。此时,需通过增加通用的checkUser逻辑来判断当前用户是否有权访问这些页面:
directives.directive('checkUser', ['$rootScope', '$location', 'userSrv', function ($root, $location, userSrv) {
return {
link: function (scope, elem, attrs, ctrl) {
$root.$on('$routeChangeStart', function(event, currRoute, prevRoute){
if (!prevRoute.access.isFree && !userSrv.isLogged) {
// reload the login route
}
/*
* IMPORTANT:
* It's not difficult to fool the previous control,
* so it's really IMPORTANT to repeat the control also in the backend,
* before sending back from the server reserved information.
*/
});
}
}
}]);
该Directive注册在rootScope上,并监听routeChangeStart事件。这也体现了一种AOP(面向切面编程)的概念:在路由切换发生前,织入一个切面来判断用户身份权限,从而在AngularJS中实现完整的身份验证逻辑。
以上介绍了AngularJS身份验证的实现方法,供参考。