前面帖子身份验证中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修改过]