在web中很多时候都能应用到身份认证,本文介绍了AngularJS 应用身份认证的技巧,废话不多说了一起往下看吧。
身份认证
最普遍的身份认证方式就是用用户名(或 email)和密码做登陆操作。这就意味要实现一个登陆的表单,以便用户能够用他们个人信息登陆。这个表单看起来是这样的:
既然这个是 Angular-powered 的表单,我们使用 ngSubmit 指令去触发上传表单时的函数。注意一点的是,我们把个人信息传入到上传表单的函数,而不是直接使用 $scope.credentials 这个对象。这样使得函数更容易进行 unit-test 和降低这个函数与当前 Controller 作用域的耦合。这个 Controller 看起来是这样的:
我们注意到这里是缺少实际的逻辑的。这个 Controller 被做成这样,目的是使身份认证的逻辑跟表单解耦。把逻辑尽可能的从我们的 Controller 里面抽离出来,把他们都放到 services 里面,这是个很好的想法。AngularJS 的 Controller 应该只管理 $scope 里面的对象(用 watching 或者 手动操作)而不是承担过多过分重的东西。
通知 Session 的变化
身份认证会影响整个应用的状态。基于这个原因我更推荐使用事件(用 $broadcast)去通知 user session 的改变。把所有可能用到的事件代码定义在一个中间地带是个不错的选择。我喜欢用 constants 去做这个事情:
constants 有个很好的特性就是他们能随便注入到别的地方,就像 services 那样。这样使得 constants 很容易被我们的 unit-test 调用。constants 也允许你很容易地在随后对他们重命名而不需要改一大串文件。同样的戏法运用到了 user roles:
如果你想给予 editors 和 administrators 同样的权限,你只需要简单地把 ‘editor' 改成 ‘admin'。
The AuthService
与身份认证和授权(访问控制)相关的逻辑最好被放到同一个 service:
return $http
.post('/login',credentials)
.then(function (res) {
Session.create(res.data.id,res.data.user.id,res.data.user.role);
return res.data.user;
});
};
authService.isAuthenticated = function () {
return !!Session.userId;
};
authService.isAuthorized = function (authorizedRoles) {
if (!angular.isArray(authorizedRoles)) {
authorizedRoles = [authorizedRoles];
}
return (authService.isAuthenticated() &&
authorizedRoles.indexOf(Session.userRole) !== -1);
};
return authService;
})
为了进一步远离身份认证的担忧,我使用另一个 service(一个单例对象,using the service style)去保存用户的 session 信息。session 的信息细节是依赖于后端的实现,但是我还是给出一个较普遍的例子吧:
一旦用户登录了,他的信息应该会被展示在某些地方(比如右上角用户头像什么的)。为了实现这个,用户对象必须要被 $scope 对象引用,更好的是一个可以被全局调用的地方。虽然 $rootScope 是显然易见的第一个选择,但是我尝试克制自己,不过多地使用 $rootScope(实际上我只在全局事件广播使用 $rootScope)。用我所喜欢的方式去做这个事情,就是在应用的根节点,或者在别的至少高于 Dom 树的地方,定义一个 controller 。 标签是个很好的选择:
ApplicationController 是应用的全局逻辑的容器和一个用于运行 Angular 的 run 方法的选择。因此它要处于 $scope 树的根,所有其他的 scope 会继承它(除了隔离 scope)。这是个很好的地方去定义 currentUser 对象:
$scope.currentUser = user;
};
})
我们实际上不分配 currentUser 对象,我们仅仅初始化作用域上的属性以便 currentUser 能在后面被访问到。不幸的是,我们不能简单地在子作用域分配一个新的值给 currentUser 因为那样会造成 shadow property。这是用以值传递原始类型(strings, numbers, booleans,undefined and null)代替以引用传递原始类型的结果。为了防止 shadow property,我们要使用 setter 函数。如果想了解更多 Angular 作用域和原形继承,请阅读 Understanding Scopes。
访问控制
身份认证,也就是访问控制,其实在 AngularJS 并不存在。因为我们是客户端应用,所有源码都在用户手上。没有办法阻止用户篡改代码以获得认证后的界面。我们能做的只是显示控制。如果你需要真正的身份认证,你需要在服务器端做这个事情,但是这个超出了本文范畴。
限制元素的显示
AngularJS 拥有基于作用域或者表达式来控制显示或者隐藏元素的指令: ngShow, ngHide, ngIf 和 ngSwitch。前两个会使用一个