使用node.js开发的token用户验证的网站很多,典型的网站,如猪八戒威客网站,使用了node.js技术。
在网站应用中,会为不同的用户赋予不同的权限(比如为管理员账户赋予较高的权限),所以我们需要在auto()函数中添加用户认证的流程。
一般来讲,最常见的方案是基于cookie 或session 授权管理,但某些场景下这种方案并不适用,比如对要求使用REST 架构的应用,或客户端对cookie\session 支持不佳(如移动端)等。更有效的方案是在每次请求中都携带token(比较常见的OAuth2.0 协议3),并在服务端通过token 进行独立的认证。这里既可以把token 字段加载到请求参数中,也可以添加到HTTP 请求头中。当然这里也可以是其他认证信息,比如E-mail 和密码、API 密钥、API 密码等。
在我们的示例中,每个请求都会提交token 字段,并在接收时把token(通过req.query.token 获取)和应用中储存的token(通常使用数据库储存,或如本例中简单地保存在SECRET_TOKEN 常量中)进行比对。如果比对通过则调用next()方法继续后续处理,如果不通过则调用next(error)触发Express.js 的错误响应:
- var auth = function(req, res, next) {
- if (req.query.token && token === SECRET_TOKEN) {
- // 校验通过,进行下一阶段处理
- return next();
- } else {
- return next(new Error('Not authorized'));
- // 也可以 res.send(401);
- }
- };
在实践中,一般使用API 的key 和secret 生成HMAC-SHA1(一种基于散列的信息加密算法)字符串,并把它和接收到的token(req.query.token)进行比对。
注意 在调用next()方法时传入一个error 对象作为参数,表示放弃请求处理,这时会触发Express.js 的错误模式,并进入错误处理流程。
我们刚才介绍了REST API 中常用的基于token 的认证模式。另外一种常见模式是使用cookie 进行用户认证,这种模式在含有用户界面的应用中经常使用。我们使用cookie 储存session ID,并在请求时自动提交。从某种意义上讲,cookie 有些类似于token,但是cookie使用较为方便,并不需要开发者做太多的工作。基于session 的认证就是使用这种模式。基于session 的认证在Web 应用中十分常见,也更受推崇,因为浏览器可以自动处理带有session 的请求头,而且大多数的后端平台或框架也能原生支持session。接下来,就让我们一起进入在Node.js 中实现基于session 的用户认证这一小节吧。
基于session 的用户认证
基于session 的用户认证借助于请求体对象req 中的session 对象完成。简单地说,session 可以鉴别客户端,并对应地储存信息,供同一客户端所有的后续请求读取。
在Express.js 4.x 版(4.1.2 版以及写本书时使用的4.2.0 版)中,我们需要手动引入(require())操作session 所依赖的模块,因为Express.js 4.x 把它们从核心包中剔除了。例如,引入并使用cookie-parser 和express-session 模块:
- var session = require('express-session');
- ...
- app.use(cookieParser());
- app.use(session());
当然,在进行这些操作之前,cookie-parser 模块和express-session 模块需要通过NPM 安装到项目的node_modules 文件夹中。
如果是在经典的Express.js 3.x 版本中,则需要在配置文件中加入下面两个中间件。
1. express.cookieParser():解析发送的和接收的cookie。
2. express.session():在每个请求体中暴露res.session 对象,并且在内存或持久化存储中(如MongoDB、Redis 等)储存session 数据。
在后文的例子中,如果没有特别提及Express.js 的版本,就表示代码能兼容3.x 和4.x
版本。
啰唆一句,我们可以在req.session 中储存任何数据,它们会自动出现在来自同一个客户端的所有后续请求中(在客户端支持cookie 的前提下)。在这个例子中,认证信息用session 储存的一个标记(布尔值),我们在授权函数中去检查这个标记,为真放行,为假则退出。像这样敲击代码:
- app.post('/login', function(req, res, next) {
- // 检查凭证
- // 在请求的有效负载中进行传递
- if (checkForCredentials(req)) {
- req.session.auth = true;
- res.redirect('/dashboard'); // 非公开内容
- } else {
- res.send(401); // 认证不通过
- }
- });
警告 避免在cookie 中储存任何敏感信息。因为cookie 十分不安全,而且储存长度存在限制(不同的浏览器限制不同,IE 最小)。所以推荐的方法是:不去手动操作cookie,cookie 中只保留session ID 字段,这个字段由Express.js 中间件自动控制。
Express.js 默认使用内存来储存session 数据,这就表示每次应用崩溃或手动重启时session 数据都会丢失。我们可以使用Redis 或者MongoDB 储存session 数据,这样既可以保证session 数据能够持久化存储也可以实现session 数据可跨服务器读取
你的博客中实现基于session 的用户认证,我们需要完成以下步骤:
1. 在app.js 的配置部分中增加引入和使用session 中间件的代码。
2. 实现一个基于session 的用户认证中间件,以便我们在多个路由规则之间复用这些代码。
3. 在app.js 文件中添加上一步骤中的中间件,以控制非公开页面的访问。像这样:
app.get('/api/', authorize, api.index)
4. 在user.js 中实现包含认证过程的登录路由POST /login 和登出路由GET/logout。