Angular.JS 与node.JS架构中基于token身份验证实现。

前面帖子身份验证中Cookies与 Tokens比较已经说明令牌比cookie好,下面是AngularJS中的实现。

服务器后端
首先安装express-jwt:
$ npm install express-jwt

配置express保护/api的每个调用:
var expressJwt = require('express-jwt');

// We are going to protect /api routes with JWT
app.use('/api', expressJwt({secret: secret}));

app.use(express.json());
app.use(express.urlencoded());

Angular应用使用用户证书通过AJAX提交POST:


app.post('/authenticate', function (req, res) {
//TODO validate req.body.username and req.body.password
//if is invalid, return 401
if (!(req.body.username === 'john.doe' && req.body.password === 'foo[author]bar[/author]')) {
res.send(401, 'Wrong user or password');
return;
}

var profile = {
first_name: 'John',
last_name: 'Doe',
email: 'john@doe.com',
id: 123
};

// We are sending the profile inside the token
var token = jwt.sign(profile, secret, { expiresInMinutes: 60*5 });

res.json({ token: token });
});

下面是调用 /api/restricted获得一个资源,其证书检查由expressJwt中间件执行。


app.get('/api/restricted', function (req, res) {
console.log('user ' + req.user.email + ' is calling /api/restricted');
res.json({
name: 'foo'
});
});

前端Angular.js
客户端首先用AngularJS 获得JWT的令牌,为了这样做我们还需要用户证书,我们将将创建一个表单让用户输入他们的用户名和密码。


<div ng-controller="UserCtrl">
<span></span>
<form ng-submit="submit()">
<input ng-model="user.username" type="text" name="user" placeholder="Username" />
<input ng-model="user.password" type="password" name="pass" placeholder="Password" />
<input type="submit" value="Login" />
</form>
</div>

一个控制器处理提交的action:


myApp.controller('UserCtrl', function ($scope, $http, $window) {
$scope.user = {username: 'john.doe', password: 'foo[author]bar[/author]'};
$scope.message = '';
$scope.submit = function () {
$http
.post('/authenticate', $scope.user)
.success(function (data, status, headers, config) {
$window.sessionStorage.token = data.token;
$scope.message = 'Welcome';
})
.error(function (data, status, headers, config) {
// Erase the token if the user fails to log in
delete $window.sessionStorage.token;

// Handle login errors here
$scope.message = 'Error: Invalid user or password';
});
};
});

现在我们已经有JWT存储在sessionStorage中,如果这个令牌被设置,我们可以为每个发出请求设置Authorization 头部,只要用$http方式即可,头部值部分使用Bearer<token>



myApp.factory('authInterceptor', function ($rootScope, $q, $window) {
return {
request: function (config) {
config.headers = config.headers || {};
if ($window.sessionStorage.token) {
config.headers.Authorization = 'Bearer ' + $window.sessionStorage.token;
}
return config;
},
response: function (response) {
if (response.status === 401) {
// handle the case where the user is not authenticated
}
return response || $q.when(response);
}
};
});

myApp.config(function ($httpProvider) {
$httpProvider.interceptors.push('authInterceptor');
});

尽管sessionStorage并不是所有浏览器支持,你可以使用 polyfill替代cookie保存在localStorage,这个数据能够持久到浏览器标签页关闭。

最后,我们能够向一个授权资源发出请求:


$http({url: '/api/restricted', method: 'GET'})
.success(function (data, status, headers, config) {
console.log(data.name); // Should log 'foo'
});

服务器后端控制台会显示:
user foo@bar.com is calling /api/restricted

本案例源码下载:github
[该贴被banq于2014-01-08 13:35修改过]