使用Express.JS中的动态URL进行HTTP身份验证

时间:2013-05-14 04:53:40

标签: node.js express http-authentication

我的Express 3.0应用程序中有一条路由定义如下:

app.post('/:app/find', auth, function(req, res){

我希望它使用HTTP授权进行限制。我的auth函数看起来像这样:

var auth = express.basicAuth(function(user, pass, callback){
    callback(null, user === 'user' && pass === 'pass');
});

我希望auth函数根据URL中的req.params.app进行数据库查找,并相应地对用户进行身份验证。问题是我还不确定如何访问它,因为app.post运行auth时尚未调用{{1}}中定义的函数。我在哪里可以得到它?是否应该使用HTTP身份验证的不同实现?

2 个答案:

答案 0 :(得分:4)

使用express.basicAuth没有正确的方法(我还查看了其他一些提供类似功能的模块,但它们似乎也从未将req传递给身份验证功能),但您可以创建一个中间件,为每个要进行身份验证的请求实例化它:

var express = require('express');
var app     = express();

var basicAuth = express.basicAuth;
var auth      = function(req, res, next) {
  ...here you can access 'req'...
  basicAuth(function(user, pass, callback) {
    ...here you can use variables set in the block above...
    callback(null, user === 'user' && pass === 'pass');
  })(req, res, next);
};

app.get('/', auth, function(req, res) {
  res.send('authenticated!');
});

app.listen(3012);

答案 1 :(得分:0)

我在StackOverflow上找到了a similar question,但在提供答案时遇到了一些麻烦。我将它转换为中间件,以使其更适合:

var includeAuth = function(req, res, next){
    var header = req.header('authorization', false);
    if(header){
        var token = header.split(/\s+/).pop() || '';
        if(token.length > 0){
            var auth = new Buffer(token, 'base64').toString(),
                parts = auth.split(/:/);
            req.auth = {user: parts[0], pass: parts[1]};
        }else
            req.auth = false;
    }else
        req.auth = false;
    next();
}

app.configure(function(){
    app.use(includeAuth);
});

...但req.header.authorization从未设定过。我很难过,直到遇到this gist,这暗示即使客户端发送授权标题,我仍然需要请求它们。所以我补充说:

if(req.auth){
        // Do things
}else{
    res.setHeader('WWW-Authenticate', 'Basic realm="Secure Area"');
    res.statusCode = 401;
    res.end("NO_CREDENTIALS");
}

然后我开始在req.auth中看到数据,但该对象具有“username”和“password”属性,而不是我指定的“user”和“pass”。再多一点之后,我发现......那些是由Express自动放在那里的!或者看起来似乎无论如何。我创建了一个没有任何中间件或其他装饰的新应用程序,只要我发送了WWW-Authenticate标头,就得到了相同的结果。这使我的includeAuth中间件功能无效。有了我新发现的知识,我现在可以使用以下内容在客户端尝试进行身份验证时向客户端提供特定的错误消息:

function verifyAuth(req, res, appName, callback){
    if(req.auth){
        db.apps.findOne({id: appName}, function(err, app){ // Call to MongoDB
            if(!err && app){
                if(app.user === req.auth.username && app.pass === req.auth.password)
                    callback(null, true);
                else
                    callback("INVALID_CREDENTIALS", false);
            }else
                callback("NO_SUCH_APP", false);
        });
    }else{
        res.setHeader('WWW-Authenticate', 'Basic realm="Secure Area"');
        res.statusCode = 401;
        res.end(JSON.stringify({success: false, error: "NO_CREDENTIALS"}));
        callback(null, false);
    }
}

app.post('/:app/find', function(req, res){
    verifyAuth(req, res, req.params.app, function(err, allowed){
        if(!err && allowed){
            // Do some things
        }else if(err)
            res.send({success: false, error: err});
    });
});

然而,我要给罗伯特一个正确的答案,因为他的解决方案不仅运作完美,而且使用更少(和更简单)的代码到达那里,并且如果他们绊倒,它将更好地适用于其他人的项目在这个问题上。