Node.js HTTP流请求与Express.js请求对象的区别

5
这是一个典型的“不破坏已有功能”的故事。
我使用Node.js创建了一个相对简单的HTTP请求处理程序。我通过将请求正文的SHA-1与作为请求标头的签名进行匹配来进行请求验证:
var http = require('http');
var crypto = require('crypto');
var secret = process.env.MY_SECRET;

var requestListener = function(req, res) {
    if (req.method === 'POST') {
        var body = '';
        req.on('data', function(data) {
            body += data;
        });
        req.on('end', function() {
            var signature = req.headers['x-signature'];
            var hash = crypto.createHmac('sha1', secret)
                .update(body)
                .digest('hex')
                .toUpperCase();
            if (signature === hash) {
                // request is authorized
            }
        });
    }
};

var server = http.createServer(requestListener);
server.listen(3000);

这种方法虽然可行,但是界面丑陋,而且我还想实现其他Express.js的功能。因此我将代码改写如下:

var crypto = require('crypto');
var express = require('express');
var app = express();
var secret = process.env.MY_SECRET;

app.use(function(req, res, next) {
    var signature = req.get('x-signature');

    var hash = crypto.createHmac('sha1', secret)
        .update(req.body)
        .digest('hex')
        .toUpperCase();

    if (signature === hash) {
        next();
    } else {
        // unauthorized
    }    
});

app.post('/', function(req, res) {
    // request is authorized
});

app.listen(3000);

当然,加密方法无法运行,因为req.body既不是字符串也不是缓冲区。但我该如何解决这个问题?
我添加了一些中间件:
app.use(bodyParser.json());

然后使用JSON.stringify将结果转换为字符串。这允许加密方法运行,然而哈希和签名并不匹配!

当使用中间件如body-parser时,Express是否对请求体进行其他操作?这对我来说没有任何意义,但也许我错过了什么。


这是一个经典的故事,即“不破不修”。如果我们都已经吸取了教训就好了。但我怀疑我永远也学不会。 - Codebling
3个回答

2

使用body parser

var options = {
  inflate: true,
  limit: '100kb',
  type: 'application/octet-stream'
};

app.use(bodyParser.raw(options));

然后您可以使用。
app.post(routeName, (req, res) => {
  let body = '';
  req.on('data', (data) => {
    body += data;
    console.log(data)
  });
  req.on('end', () => {
    fs.appendFile(`./${fileName}.log`, '\n' + body, (err) => {
      if (err) throw err;
    });
  });
  res.end();
})

如果您正在使用请求流,请使用fs.createWriteStream来写入文件。无论如何,感谢指出bodyParser.raw。 - The Fool

1
发现了一个解决方案。我刚刚创建了一个自定义的请求体解析器。
function(req, res, next) {
    req.setEncoding('utf8');
    req.rawBody = '';
    req.on('data', function(chunk) {
        req.rawBody += chunk;
    });
    req.on('end', function(){
        next();
    });
}

我仍然不明白为什么bodyParser.text()不能工作得和原来一样!


0
你在POST请求的正文中发送了什么?
我已经使用你的算法签署了这个字符串:nodejs,结果是:92FCFCFBCDB06B40F76FEE4E6271EFC2554290FD 然后我用curl调用了服务器:
curl --header "x-signature: 92FCFCFBCDB06B40F76FEE4E6271EFC2554290FD" --data "something=nodejs" http://localhost:4040

这是我的服务器文件:
var express = require('express');
var app = express();

var bodyParser = require('body-parser');

var secret = 'something';
var crypto = require('crypto');

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));

app.use(function(req, res, next) {
  var signature = req.get('x-signature');
  var hash = crypto
    .createHmac('sha1', secret)
    .update(req.body.something)
    .digest('hex')
    .toUpperCase();

  if (signature === hash) {
    next();
  } else {
    res.send('you do not have permission');
  }
});

app.post('/', function(req, res) {
  res.send('hey');
});

app.listen(4040, function() {
  console.log('server up and running at 4040 port');
});

如果签名无效,您将看到一条消息,上面写着:“您没有权限”,但如果您发送有效的签名,您将能够使用POST路由/

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接