详解Angular开发中的登陆与身份验证(2)

对于 Loading 动画,是在 AppController 之外的,可以在 AppController 的代码中,对其进行隐藏。这样达到了所有 CSS / JavaScript 加载完成之后 Loading 就消失的目的。

AppController 中有一个变量 isUserAuth ,初始化的时候是 false ,当本地存储的会话信息验证有效,或者登陆完成之后,这个值便会置为 ture ,由于 ng-if 的控制,便可以实现隐藏登陆表单、显示应用内容的目的。要注意,这里只有使用 ng-if 而不是 ng-show/ng-hide ,前者才会真正的删除和增加 DOM 元素,而后者只是修改某个 DOM 元素的 CSS 属性,这点很重要,只有这样,才能够保证登陆完成之后,再加载单页应用中的内容,防止还没有登陆,当前路由中的控制器代码就直接执行了。

为什么客户端也要加密密码

一个比较理想的基于用户名和密码的登陆流程是这样的:

1.浏览器端获取用户输入的密码,使用 MD5 一类的哈希算法,生成固定长度的新密码,如 md5(username + md5(md5(password))) ,再将密码哈希值和用户名提交给后端

2.后端根据用户名获取对应的盐,使用用户名和密码哈希值,算出一个密文,根据用户名和密文去数据库查询

3.如果查询成功,则生成密钥,返回给浏览器,并执行第 4 步

4.后端生成新的盐,根据新的盐和浏览器提交的密码哈希值,生成新的密文。在数据库中更新盐和密文

可能有 80% 的人无法理解为什么要把一个登陆做得这么复杂。这可能要写一篇专门的文章才解释得清楚。在这里先解释一下为什么浏览器端要对密码做哈希,原因如下:

1.从源头上保护用户的密码,保证只有做按键记录才可以拿到用户的原始密码
    2.就算网络被窃听,又没有使用 https ,那么被偷走的密码,也只是哈希之后的,最多影响用户在这个服务器里的数据,而不影响使用相同密码的其它网站
    3.就算是服务器的所有者,都无法获取用户的原始密码
这种做法,使得用户的最大风险,也只是当前这个应用中的数据被窃取。不会扩大损失范围,绝不会出现 CSDN 之流的问题。

登陆成功的通知

对于有些应用,并不是所有的页面都需要用户登陆的,可能是进行某些操作的时候,才需要登陆。在这种情况下,登陆完成之后,必须要通知整个应用。这可以使用广播这个功能。

简易代码如下:

angular .module('app') .controller('LoginController', ['$rootScope', LoginController]); function LoginController($rootScope) { // 登陆成功之后调用的函数 function afterLoginSuccess() { $rootScope.$broadcast('user.login.success', { // 需要传输的数据 }); } }

在其它的控制器中,便可以监听这个广播,并执行登陆成功之后需要进行的操作,如获取列表或者详情:

$scope.$on('user.login.success', function(handle, data){ // 处理 });

身份验证信息

登陆成功之后,服务器返回了密钥,之后的 API 请求都需要带上密钥,而且请求返回的响应,还需要检查是否是关于身份信息失效的错误。这一系列的工作比较繁琐,应该是自动完成才行。

保存

密钥的保存,大概有如下几个办法:

1.Cookies:前面已经提到了,这个并不推荐使用。同时,它还有最大 4k 的限制

2.sessionStorage:tab 页内有效,一旦关闭,或者打开了新的 tab 页,sessionStorage 是不能共享的

3.localStorage:较为理想的存储方式,除非清理浏览器数据,否则 localStorage 存储的数据会一直存在

4.Angular 单例 Service:存储在应用之内得话,刷新后数据会丢失,当然也不能 tab 页之间共享
比较好的办法是,身份验证信息存储在 localStorage 里,但在应用启动时,初始化到 Angular 的单例 Service 中。

在请求中加入身份验证信息

身份验证信息的目的,是为了向服务器表明身份,获取数据。所以,在请求中需要附加身份验证信息。

一般的应用中,身份验证信息都是放在请求的 headers 头部中。如果在每次请求的时候,一一设置 headers ,那就太费时费力了。Angular 中的 $httpProvider 提供了一个拦截器 interceptors ,通过它可以实现对每一个请求和响应的统一处理。

添加拦截器的方式如下:

angular .module('app') .config(['$httpProvider', function($httpProvider){ $httpProvider.interceptors.push(HttpInterceptor); }]);

HttpInterceptor 的定义方式如下:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wzxspg.html