angularjs 用户认证解决方案
2015年7月30日 · 216 字 · 2 分钟
作为一个全栈ajax的mvvm框架,angularjs可谓如火如荼,可真正做到全栈ajax,首要面对的问题就是用户身份验证。
本文的身份验证不采用cookie,而采用基于http Authorize 请求头的方式验证用户,此方式能做到永远只有一个用户同时在线(服务端同一时间只会接受一个合法的token请求,其他的请求返回401)。
Service
app.factory('Authorize', function() {
return {
uid: '',
token: '',
logout: function() {
this.uid = '';
this.token = '';
localStorage.removeItem('authorize.uid');
localStorage.removeItem('authorize.token');
}
}
});
由于service是单例的。保存在service很合适,再看登录检测。
验证流程
app运行时主动读取localStorage中的authorize.uid和authorize.token字段,将这两个字段发送至后端接口验证,如果验证成功返回用户信息,验证失败返回http 401错误(未授权)。
如果localStorage没有上述两个字段,则检测url中是否有,如果有则写入本地localStorage之后发送至后端验证,如果没有,跳转至后端服务器的oauth接口进行授权拿之后,将openid和token写入queryString并回调到app页面,代码如下:
Authorize.uid = $location.search().uid || localStorage.getItem('authorize.uid');
Authorize.token = $location.search().token || localStorage.getItem('authorize.token');
if (!Authorize.uid || !Authorize.token) {
if (!Platform.isWechat) {
Authorize.uid = 1001;
Authorize.token = '2ddha3nry8';
}
else {
location.href = CONFIG.api + '/auth/oauth?callback=' + encodeURIComponent($location.protocol() + "://" + $location.host() + ":" + $location.port() + "/#" + $location.path());
return;
}
}
//写入本地
localStorage.setItem('authorize.uid', Authorize.uid);
localStorage.setItem('authorize.token', Authorize.token);
//读取用户数据
var user = User.get({uid: Authorize.uid}, function() {
$rootScope.user = user;
});
读取用户数据这边,采用的$resource服务封装,这里就不说了。
请求过程中的授权处理
接下来是比较重要的一点,如何在登陆后在每次请求头中注入Authorize信息,方法是采用拦截器。
有一个问题,如果由于刷新过快,检测用户回调还没执行完,这时候访问所有接口都是401,这里就需要在$httpProvider上注入拦截器进行请求恢复了。代码如下:
app.factory('AuthInjector', function($q, Authorize, $injector, CONFIG) {
return {
request: function(config) {
if (Authorize.token) {
config.headers.Authorization = 'Bearer ' + Authorize.token;
}
return config;
},
response: function(response) {
var defer = $q.defer();
defer.resolve(response);
return defer.promise;
},
responseError: function(error) {
//如果401且本地存在uid,则刷新accessToken
if (error.status == 401) {
//刷新请求
var $http = $injector.get("$http");
$http({
method: 'GET',
url: CONFIG.api + '/auth/token',
params: {
uid: Authorize.uid
}
}).success(function(data) {
Authorize.token = data.token;
//写入本地
localStorage.setItem('authorize.token', data.token);
return $http(error.config);
});
}
else if (error.status == 422) {
var resp;
angular.forEach(error.data, function(item) {
if (resp == undefined) {
resp = item;
}
});
return $q.reject(resp);
}
return $q.reject({
message: '请求失败'
});
}
}
});
原理就是所有的http请求一旦返回401就进行重新登录请求,最后一句
return $http(error.config);
会将之前报401错误的请求恢复并重新执行一遍。
大体就是这么多,总结一下就是:
- app.run中检测登录。
- Authorize服务保存用户信息。
- httpProvider中注入拦截器实现Authorize头的自动添加和401结果的请求恢复。