node.js json
前端框架和库(例如Ember,Angular和Backbone)是朝着更丰富,更复杂的Web应用程序客户端发展的趋势的一部分。 因此,服务器端组件无需承担许多传统职责,实际上,它们变得更像API。 这种API方法可以使应用程序的传统“前端”和“后端”部分更好地分离。 一组开发人员可以独立于前端工程师而构建后端,其另外的好处是测试变得更加简单。 例如,这种方法还使构建与Web应用程序共享相同后端的移动应用程序变得更加容易。
提供API时面临的挑战之一是身份验证。 在传统的Web应用程序中,服务器通过做两件事来响应成功的身份验证请求。 首先,它使用某种存储机制创建一个会话。 每个会话都有自己的标识符(通常是一个长的半随机字符串),用于在将来的请求中检索有关该会话的信息。 其次,该信息通过指示其设置cookie的标头发送给客户端。 浏览器将会话ID cookie自动附加到所有后续请求,从而允许服务器通过从存储中检索适当的会话来识别用户。 这就是传统的Web应用程序如何绕过HTTP无状态这一事实。
API应该设计为真正无状态的。 这意味着没有登录或注销方法,也没有会话。 API设计人员也不能依赖Cookie,因为不能保证将通过Web浏览器发出请求。 显然,我们需要一种替代机制。 本文研究了旨在解决该问题的一种可能的机制-JSON Web令牌或JWT(发音为jot)。 本文中的示例在后端使用Node's Express框架,在客户端使用Backbone。
背景
让我们简要地介绍一些保护API的常用方法。
一种是使用HTTP基本身份验证。 在正式的HTTP规范中定义,这实际上涉及在服务器响应上设置标头,以指示需要认证。 客户端必须通过将其凭据(包括密码)附加到每个后续请求中来进行响应。 如果凭据匹配,则将用户信息作为变量提供给服务器应用程序。
第二种方法非常相似,但是使用应用程序自己的身份验证机制。 这通常涉及对照存储的凭证检查提供的凭证。 与HTTP基本身份验证一样,这要求在每次调用时都提供用户凭据。
第三种方法是OAuth (或OAuth2)。 在很大程度上设计用于针对第三方服务进行身份验证,至少在服务器端实施,这可能是相当具有挑战性的。
第四种方法是使用令牌。 这就是我们将在本文中讨论的内容。 我们将研究在前端和后端都使用JavaScript的实现。
代币方式
我们可以允许客户端为令牌交换有效的凭证,而不是在每个请求中都提供诸如用户名和密码之类的凭证。 该令牌使客户端可以访问服务器上的资源。 令牌通常比密码更长,更混乱。 例如,我们要处理的JWT大约为150个字符。 获取令牌后,必须随每个API调用一起发送令牌。 但是,这比通过每个请求发送用户名和密码(甚至通过HTTPS)都更安全。
将令牌视为安全通行证。 您可以在到达时进入受限建筑的前台(提供用户名和密码)来标识自己,如果可以成功识别出您,则将获得安全通行证。 当您在建筑物中四处走动(通过调用API尝试访问资源)时,您需要出示通行证,而不是再次进行初始标识过程。
关于智威汤逊
JWT是一个规范草案 ,尽管从本质上讲它们实际上只是已经很普遍的身份验证和授权机制的更具体的实现。 交换令牌的过程。 JWT分为三个部分,各部分之间用句点分隔。 JWT是URL安全的,这意味着它们可以在查询字符串参数中使用。
JWT的第一部分是简单JavaScript对象的编码字符串表示形式,该对象描述令牌以及所使用的哈希算法。 以下示例说明了使用HMAC SHA-256的JWT。
{
"typ" : "JWT",
"alg" : "HS256"
}
编码后,对象变为以下字符串:
eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9
JWT的第二部分构成了令牌的核心。 它也代表一个JavaScript对象,其中包含一些信息。 这些字段中有些是必填字段,有些是可选字段。 下面显示了一个从规范草案中提取的示例。
{
"iss": "joe",
"exp": 1300819380,
"http://example.com/is_root": true
}
这称为JWT声明集。 为了本文的目的,我们将忽略第三个参数,但是您可以在规范中阅读更多内容。 iss
属性是issuer
缩写,它指定发出请求的个人或实体。 通常,这将是用户访问API。 exp
字段( expires
缩写)用于限制令牌的生存期。 编码后,JSON令牌如下所示:
eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ
JWT的第三个也是最后一个部分是基于标头(第一部分)和主体(第二部分)生成的签名。 我们的示例JWT的签名如下所示。
dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
生成的完整JWT如下所示:
eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
规范中支持许多其他可选属性。 其中iat
表示令牌发行的时间, nbf
(不nbf
于)表示令牌在特定时间之前不应该被接受,而aud
(听众)表示令牌预期的接收者。
处理令牌
我们将使用JWT Simple模块来处理令牌,这使我们不必深入研究编码和解码的细节。 如果您真的有兴趣,可以在规范中找到更多信息,或者通读存储库的源代码。
![](https://i-blog.csdnimg.cn/blog_migrate/de723aa763615f6c9b08285348340dab.png)
免费学习PHP!
全面介绍PHP和MySQL,从而实现服务器端编程的飞跃。
原价$ 11.95 您的完全免费
首先使用以下命令安装库。 请记住,可以通过在命令中包含--save
标志,将其自动添加到项目的package.json
文件中。
npm install jwt-simple
在应用程序的初始化部分,添加以下代码。 此代码导入Express和JWT Simple,并创建一个新的Express应用程序。 该示例的最后一行将名为jwtTokenSecret
的应用程序变量设置为值YOUR_SECRET_STRING
(确保将此值更改为其他值)。
var express = require('express');
var jwt = require('jwt-simple');
var app = express();
app.set('jwtTokenSecret', 'YOUR_SECRET_STRING');
获取令牌
我们需要做的第一件事是使客户端能够将其用户名和密码交换为令牌。 RESTful API中有两种可能的方法。 第一种是通过向身份验证端点发出POST
请求,服务器使用令牌响应成功的请求。 或者,您可以实现一个端点,客户端可以从该端点GET
令牌,这要求它们提供其凭据作为查询参数,或者更好的是通过标头提供。
本文的目的是解释身份验证令牌,而不是基本的用户名/密码身份验证机制,因此,假设我们已经具有以下内容,并且已经从请求中获取了username
和password
:
User.findOne({ username: username }, function(err, user) {
if (err) {
// user not found
return res.send(401);
}
if (!user) {
// incorrect username
return res.send(401);
}
if (!user.validPassword(password)) {
// incorrect password
return res.send(401);
}
// User has authenticated OK
res.send(200);
});
接下来,我们需要使用JWT令牌响应成功的身份验证尝试:
var expires = moment().add('days', 7).valueOf();
var token = jwt.encode({
iss: user.id,
exp: expires
}, app.get('jwtTokenSecret'));
res.json({
token : token,
expires: expires,
user: user.toJSON()
});
您会注意到jwt.encode()
函数jwt.encode()
两个参数。 第一个是将形成令牌主体的对象。 第二个是我们之前定义的秘密字符串。 使用前面描述的iss
和exp
字段构造令牌。 请注意, Moment.js用于将到期时间设置为从现在开始的7天。 res.json()
方法用于将令牌的JSON表示形式返回给客户端。
验证令牌
为了验证JWT,我们需要编写一些中间件,该中间件将:
- 检查附加令牌。
- 尝试对其进行解码。
- 检查令牌的有效性。
- 如果令牌有效,则检索相应的用户记录并将其附加到请求对象。
让我们开始创建中间件的基础知识:
// @file jwtauth.js
var UserModel = require('../models/user');
var jwt = require('jwt-simple');
module.exports = function(req, res, next) {
// code goes here
};
为了获得最大的灵活性,我们将允许客户端以以下三种方式之一附加令牌-作为查询字符串参数,表单主体参数或HTTP标头。 对于后者,我们将使用标头x-access-token
。
这是我们中间件中的代码,它试图检索令牌:
var token = (req.body && req.body.access_token) || (req.query && req.query.access_token) || req.headers['x-access-token'];
请注意,为了访问req.body
我们需要首先附加express.bodyParser()
中间件。
接下来,让我们尝试解码JWT:
if (token) {
try {
var decoded = jwt.decode(token, app.get('jwtTokenSecret'));
// handle token here
} catch (err) {
return next();
}
} else {
next();
}
如果解码过程失败,则JWT Simple包将引发异常。 如果发生这种情况,或者没有提供令牌,我们只需调用next()
继续处理请求–这仅意味着我们尚未识别用户。 如果存在有效令牌并且已对其进行解码,则我们应该以一个具有两个属性的对象结束-包含用户ID的iss
和具有过期时间戳记的exp
。 让我们先检查后者,如果令牌已过期,则拒绝它:
if (decoded.exp <= Date.now()) {
res.end('Access token has expired', 400);
}
如果令牌仍然有效,我们可以检索用户并将其附加到请求对象,如下所示。
User.findOne({ _id: decoded.iss }, function(err, user) {
req.user = user;
});
最后,将中间件附加到路由:
var jwtauth = require('./jwtauth.js');
app.get('/something', [express.bodyParser(), jwtauth], function(req, res){
// do something
});
或者,也许将其附加到一系列路线上:
app.all('/api/*', [express.bodyParser(), jwtauth]);
我们的中间件现在检查请求以寻找有效令牌,如果存在,则将用户对象附加到请求。 现在,构建一些简单的中间件来拒绝没有有效令牌的请求应该是相当简单的,尽管您可能希望将其构建到同一块中间件中。
这就是令牌方法的服务器端元素。 在下一节中,我们将研究令牌在客户端的工作方式。
客户端
我们提供了一个简单的GET
端点,用于获取访问令牌。 这很简单,我们可能不需要赘述–只需拨打电话,传递用户名和密码(也许是通过表单),如果请求成功,则将生成的令牌存储在某个地方以备后用。
我们将更详细地介绍将令牌附加到后续调用。 一种方法是使用jQuery的ajaxSetup()
方法。 这可以用于直接的Ajax调用,也可以用于在后台使用Ajax与服务器通信的前端框架。 例如,假设我们使用window.localStorage.setItem('token', 'the-long-access-token')
将访问令牌放入本地存储中; 我们可以通过标头将令牌附加到所有调用,如下所示:
var token = window.localStorage.getItem('token');
if (token) {
$.ajaxSetup({
headers: {
'x-access-token': token
}
});
}
简而言之,这将“劫持”所有Ajax请求,并且如果本地存储中有令牌,它将使用x-access-token
标头将其附加到请求。
这不能处理令牌到期,但是应该相对简单。 您会记得,我们返回了带有令牌的到期时间戳。 此外,您可能希望服务器使用指示标头必须重新认证的标头将过期的令牌通知客户端。
与骨干网一起使用
让我们将上一节中的方法应用于Backbone应用程序。 最简单的方法是如下所示全局覆盖Backbone.sync()
。
// Store "old" sync function
var backboneSync = Backbone.sync
// Now override
Backbone.sync = function (method, model, options) {
/*
* "options" represents the options passed to the underlying $.ajax call
*/
var token = window.localStorage.getItem('token');
if (token) {
options.headers = {
'x-access-token': token
}
}
// call the original function
backboneSync(method, model, options);
};
额外的安全性
您可以通过在服务器上存储已发行令牌的记录,然后在每个后续请求中将它们与该记录进行核对来增加安全性的另一层。 这将防止第三方“欺骗”令牌,并且还允许服务器使令牌无效。 我不会在这里进行介绍,但是它应该相对容易实现。
摘要
在本文中,我们研究了一些在API上进行身份验证的方法,特别是JSON Web令牌。 我们使用带有Express的Node编写了该技术的基本工作实现,并以Backbone为例,介绍了如何在客户端使用它。 GitHub上提供了本文的代码。
我们还没有完全实现该规范的更多内容,例如对资源的“声明”,但是我们所做的工作是使用基本建议来建立一种机制,用于在客户端之间交换访问令牌的凭证和JavaScript应用程序的服务器。
当然,您可以将此方法应用于其他技术,例如Ruby或PHP后端,或Ember或AngularJS应用程序。 或者,您可以将其用于移动应用程序。 例如,通过将Web技术与PhoneGap等结合使用,使用诸如Sencha之类的工具或作为完全本机的应用程序。
翻译自: https://www.sitepoint.com/using-json-web-tokens-node-js/
node.js json