Angular登陆认证小例子

使用Angular来实现登陆跳转等功能的话,其实只要在ui-routerstate中或者ngRoute中的when方法中添加对应的登陆判断变量,然后在根据相应的路由跳转事件中进行判断即可。

首先在路由设置中添加对应的变量requireLogin来断定是否登陆:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$stateProvider
.state('home',{
url: '/',
templateUrl: '/static/partials/welcome.html',
controller: 'welcomeCtrl',
data: { requireLogin: false }
})
.state('blog',{
abstract: true,
url: '/blogs',
template: '<ui-view />'
})
.state('blog.create',{
url: '/create',
templateUrl: '/static/partials/blog.create.html',
controller: 'blogCreateCtrl',
data: { requireLogin: true }
})
//...

接下来在根作用域捕获$stateChangeStart事件触发方法中判断:

1
2
3
4
5
6
7
8
9
10
$rootScope.$on('$stateChangeStart', 
function(event, toState, toParams, fromState, fromParams){
//如果requireLogin为true且不存在授权信息
if (toState.data.requireLogin && AuthService.isAuth() === false) {
//跳转到主页面
$location.path('/');
//向子作用域发送消息
$rootScope.$broadcast('token timeout');
};
});

这里定义了AuthService的服务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
myApp.factory('AuthHttpService', ['$q', '$timeout', '$http', function($q, $timeout, $http){
var sendData = function(url, method, username,password, data){
var deferred = $q.defer();
var req = {
method: method,
url: url,
data: data,
headers: {
Accept: 'application/json',
//对数据进行Base64编码
Authorization: 'Basic ' + btoa(username + ':' + password)
}
};
$timeout(function(){
$http(req).then(function(response){
deferred.resolve(response);
}, function(response){
deferred.reject(response);
});
},1000);
return deferred.promise;
};
return {
'sendData': sendData
}
}]);
myApp.factory('AuthService',
['$q', 'localStorageService', 'AuthHttpService',
function($q, localStorageService, AuthHttpService){
var login = function(user){
var deferred = $q.defer();
//访问后端获取数据
AuthHttpService.sendData('/token', 'POST', user.username, user.password, user)
.then(function(response){
if(response.status === 200){
setToken(response.data);
deferred.resolve();
}else{
deferred.reject();
}
},function(response){
deferred.reject();
});
return deferred.promise;
};

var logout = function(){
removeToken();
};

var setToken = function(data){
将token以及userId存入到本地存储中
localStorageService.set('token', data.token);
localStorageService.set('userId', data.userId);
};

var getToken = function(){
return localStorageService.get('token');
};

var getUserId = function(){
return localStorageService.get('userId');
};

var removeToken = function(){
localStorageService.remove('token');
localStorageService.remove('userId');
};
//判断是否已登陆
var isAuth = function(){
if (getToken()) {
return true;
}else{
return false;
}
};

return ({
isAuth: isAuth,
login: login,
logout: logout,
getToken: getToken,
getUserId: getUserId
});
}]);

在根作用域中,我们还需要捕获已经失效的授权,这里使用了Restangular插件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Restangular.addFullRequestInterceptor(function(headers, params, element, httpConfig){
if(!headers){
//获取验证的形式不仅为'用户名:密码',
//也可以为token,因此如果含有token,
//则也需要满足'username:password'的格式
headers = { 'Authorization': 'Basic ' + btoa(AuthService.getToken() + ':unused') };
}
headers['Authorization'] = 'Basic ' + btoa(AuthService.getToken() + ':unused');
return { headers: headers };
});
Restangular.setErrorInterceptor(function(response, deferred, responseHandler){
//判断是否授权失效
if (response.status == 401) {
AuthService.logout();
$location.path('/');
alert('token invalid,please sign in');
$rootScope.$broadcast('token timeout');
return false;
}
return true;
});

在后端使用了flask来做授权部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from flask.ext.httpauth import HTTPBasicAuth

# flask-httpauth
auth = HTTPBasicAuth()

# 登陆获取token
@app.route('/token', methods=['POST'])
@auth.login_required
def get_auth_token():
token = g.user.generate_auth_token()
return jsonify({ 'token': token.decode('ascii'), 'userId': g.user.id })

# 验证,会自动进行base64转码,因此需要将传入的参数以"base64(email_or_token:password)"
# 进行编码
@auth.verify_password
def verify_password(username_or_token, password):
user = User.verify_auth_token(username_or_token)
# 不是token登陆则以正常用户名密码登录
if not user:
md5 = hashlib.md5()
md5.update(password.encode('utf-8'))
hash_password = md5.hexdigest()
user = User.query.filter_by(username=username_or_token)\
.filter_by(password=hash_password).first()
if not user:
return False
g.user = user
return True

@app.route('/blog/create', methods=['POST'])
@auth.login_required
def creaetBlog():
pass

这里面使用了Flask-HTTPAuth来进行HTTP BASIC Authentication的方法来进行验证。
在标注了`@auth.login_required的注解的方法则表明需要让Flask-HTTPAuth需要验证用户信息。通过实现verify_password回调函数去验证用户名和密码,然后Flask-HTTPAuth再调用这个回调函数,这样就验证用户是否授权了。 当通过授权的用户信息则是保存在应用上下文g对象中,这样其他函数就可以调用到登陆的用户信息,相当于存在session`中。


参考
Handling User Authentication With Angular and Flask
AngularJS User Registration and Login Example & Tutorial
Authentication made simple in Single Page AngularJS Applications