【问题标题】:Make callback block the thread使回调阻塞线程
【发布时间】:2016-08-05 05:36:52
【问题描述】:

我正在使用 ExpressJS 中间件检查数据库中的用户 ip,如果过去一小时内登录失败超过 7 次,则停止响应用户,如果一切正常,我在数据库连接之前使用检查“/”以不向数据库发送垃圾邮件。但事实证明,当中间件访问数据库并检查回调时,第一个 else 中的代码会运行。这是我的中间件:

// check for failed logins from this ip in db
// if allowed number exceeded - stop responding
app.use(function (req, res, next) {
if(req._parsedUrl.pathname === '/') {
    MongoClient.connect(databaseUri || 'mongodb://localhost:27017/dev', function (err, db) {
        assert.equal(err, null);
        var failedLogins = db.collection('FailedLogins');
        failedLogins.find({ip: req._remoteAddress}).toArray(function (err, results) {
            assert.equal(err, null);
            console.log('db check');
            // if ip is in FailedLogins collection
            if (results.length) {
                // if there are more than 7 logins and they haven't expired
                if (results[0].failedLoginsNum >= 7 && parseInt(results[0].expiration) >= parseInt(Date.now())) {
                    res.end();
                } else {
                    next();
                }
            } else {
                next();
            }
        });
    });
} else {
    console.log('next');
    next();
}
});

这是控制台输出:

db check
GET / 200 20.117 ms - -
next
GET /favicon.ico 200 207.559 ms - 1353

【问题讨论】:

    标签: node.js express callback


    【解决方案1】:

    这是预期的行为,因为在建立 db 连接之前收到第二个请求并完成查找查询。

    首先,您不想在每个请求上都创建新连接,这是一种不好的做法。

    考虑下一个代码:

    dbProvider.js

    'use strict';
    
    const MongoClient = require('mongodb').MongoClient;
    const CONNECTION_PATH = 'mongodb://localhost:27017/dev';
    
    module.exports.init = cb => {
      MongoClient.connect(CONNECTION_PATH, (err, db) => {
        module.exports.db = db;
        cb(err, db);
      });
    };
    

    index.js

    'use strict';
    
    const express = require('express');
    const app = express();
    
    const dbProvider = require('./dbProvider');
    const PORT = 8888;
    
    dbProvider.init((err, db) => {
      if(err) {
        console.error(err);
        process.exit(1);
      }
    
      app.listen(PORT, () => console.log(`Listening on port ${PORT}`));
    });
    

    在 index.js 中,我们等待连接建立,然后才监听 HTTP 请求。 下一步是登录,所以如果你想限制登录尝试,我建议使用简单的中间件。

    index.js 修改

    'use strict';
    
    const express = require('express');
    const app = express();
    
    const dbProvider = require('./dbProvider');
    const PORT = 8888;
    
    dbProvider.init((err, db) => {
      if(err) {
        console.error(err);
        process.exit(1);
      }
    
      app.listen(PORT, () => console.log(`Listening on port ${PORT}`));
    });
    
    // middlewares
    
    function checkAuth(req, res, next) {
      // here check if userId in cookie and match to db record or check token
      if (req.authenticated()) {
        return next();
      } else {
        return res.send(401); // Respond "Unauthorized"
      }
    }
    
    function checkLoginAttempts(req, res, next) {
      let failedLogins = dbProvider.db.collection('FailedLogins');
      failedLogins.find({ip: req._remoteAddress}).toArray(function (err, results) {
        if(err) {
          console.error(err);
          return res.status(500).end();
        }
        console.log('db check');
    
        if(!results.length) return next();
    
        // if ip is in FailedLogins collection
        // if there are more than 7 logins and they haven't expired
        if (results[0].failedLoginsNum >= 7 && parseInt(results[0].expiration) >= parseInt(Date.now())) {
          res.status(401).send('The maximum number of login attempts has been reached. Please try again in 1 hour.');
        } else {
          next();
        }
      });
    }
    
    // routes
    
    app.use('/yourLoginEndPoint', checkLoginAttempts, (req, res) => {
      // login process, set userId as cookie value or create token
    });
    
    app.use('/anyOtherEndPoint', checkAuth, (req, res) => {
      // here you sure that user is authenticated
    });
    

    如果有任何问题,请告诉我

    【讨论】:

    • 谢谢,你的实现看起来比我的好多了,我会用它作为指导来解决我所做的烂摊子)
    • 不客气,仅供参考,有一个名为 passportJs 的身份验证库被广泛使用,它支持许多 Oauth 策略,如 Google、facebook 等。对于 mongoDb,有一个 mongoose 库,它具有更多功能,例如创建模式、预保存挂钩等。
    猜你喜欢
    • 2013-11-16
    • 2018-02-18
    • 1970-01-01
    • 2013-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-26
    • 1970-01-01
    相关资源
    最近更新 更多