【问题标题】:Basic HTTP authentication with Node and Express 4使用 Node 和 Express 4 进行基本 HTTP 身份验证
【发布时间】:2014-06-30 05:58:20
【问题描述】:

使用 Express v3 实现基本 HTTP 身份验证似乎很简单:

app.use(express.basicAuth('username', 'password'));

版本 4(我使用的是 4.2)删除了 basicAuth 中间件,所以我有点卡住了。我有以下代码,但它不会导致浏览器提示用户输入凭据,这是我想要的(以及我想象的旧方法所做的):

app.use(function(req, res, next) {
    var user = auth(req);

    if (user === undefined || user['name'] !== 'username' || user['pass'] !== 'password') {
        res.writeHead(401, 'Access invalid for user', {'Content-Type' : 'text/plain'});
        res.end('Invalid credentials');
    } else {
        next();
    }
});

【问题讨论】:

  • 无耻插件:我维护了一个相当流行的模块,它使这变得容易并且具有您需要的大多数标准功能:express-basic-auth
  • 我最近 fork @LionC 的包,因为我必须在超短的时间内为公司项目调整它(启用上下文感知授权):npmjs.com/package/spresso-authy
  • @LionC 从express-basic-auth 的文档中不清楚如何将其应用于一条路线。

标签: node.js http express http-authentication


【解决方案1】:

我使用原始basicAuth的代码来寻找答案:

app.use(function(req, res, next) {
    var user = auth(req);

    if (user === undefined || user['name'] !== 'username' || user['pass'] !== 'password') {
        res.statusCode = 401;
        res.setHeader('WWW-Authenticate', 'Basic realm="MyRealmName"');
        res.end('Unauthorized');
    } else {
        next();
    }
});

【讨论】:

  • 此模块已被弃用,请改用jshttp/basic-auth(相同的api,因此答案仍然适用)
【解决方案2】:

许多中间件在 v4 中从 Express 核心中提取出来,并放入单独的模块中。基本认证模块在这里:https://github.com/expressjs/basic-auth-connect

您的示例只需要更改为:

var basicAuth = require('basic-auth-connect');
app.use(basicAuth('username', 'password'));

【讨论】:

  • 该模块声称已弃用(尽管它建议的替代方案似乎不令人满意)
  • ^^ 绝对不令人满意,因为在密集无证。用作中间件的零示例,它可能适用,但调用不可用。他们给出的示例非常适合通用性,但不适用于使用信息。
  • 是的,这个已经被弃用了,虽然推荐的文档很少,但代码很简单github.com/jshttp/basic-auth/blob/master/index.js
  • 我已经在this answer中描述了如何使用basic-auth
  • 基于在代码中以明文形式输入密码,整个模块如何存在??至少通过在 base64 中进行比较来掩盖它似乎稍微好一些。
【解决方案3】:

我在express 4.0中更改了http-auth的基本认证,代码是:

var auth = require('http-auth');

var basic = auth.basic({
        realm: "Web."
    }, function (username, password, callback) { // Custom authentication method.
        callback(username === "userName" && password === "password");
    }
);

app.get('/the_url', auth.connect(basic), routes.theRoute);

【讨论】:

  • 这是真正的即插即用。很好。
【解决方案4】:

似乎有多个模块可以做到这一点,有些已被弃用。

这个看起来很活跃:
https://github.com/jshttp/basic-auth

这是一个使用示例:

// auth.js

var auth = require('basic-auth');

var admins = {
  'art@vandelay-ind.org': { password: 'pa$$w0rd!' },
};


module.exports = function(req, res, next) {

  var user = auth(req);
  if (!user || !admins[user.name] || admins[user.name].password !== user.pass) {
    res.set('WWW-Authenticate', 'Basic realm="example"');
    return res.status(401).send();
  }
  return next();
};




// app.js

var auth = require('./auth');
var express = require('express');

var app = express();

// ... some not authenticated middlewares

app.use(auth);

// ... some authenticated middlewares

确保将auth 中间件放在正确的位置,之前的任何中间件都不会被验证。

【讨论】:

  • 我实际上赞成'basic-auth-connect',名字不好但功能上它比'basic-auth'好。后者所做的只是解析授权标头。您仍然需要自己 implement 协议(也就是发送正确的标头)
  • 完美!这次真是万分感谢。这很有效,很好地解释了一切。
  • 我试过这个,但它只是一直要求我通过连续循环登录。
【解决方案5】:

使用原生 JavaScript 的简单基本身份验证(ES6)

app.use((req, res, next) => {

  // -----------------------------------------------------------------------
  // authentication middleware

  const auth = {login: 'yourlogin', password: 'yourpassword'} // change this

  // parse login and password from headers
  const b64auth = (req.headers.authorization || '').split(' ')[1] || ''
  const [login, password] = Buffer.from(b64auth, 'base64').toString().split(':')

  // Verify login and password are set and correct
  if (login && password && login === auth.login && password === auth.password) {
    // Access granted...
    return next()
  }

  // Access denied...
  res.set('WWW-Authenticate', 'Basic realm="401"') // change this
  res.status(401).send('Authentication required.') // custom message

  // -----------------------------------------------------------------------

})

注意:这个“中间件”可以在任何处理程序中使用。只需删除 next() 并反转逻辑即可。请参阅下面的 1-statement 示例,或此答案的 edit history

为什么?

  • req.headers.authorization 包含值“Basic <base64 string>”,但它也可以为空,我们不希望它失败,因此 || '' 的奇怪组合
  • 节点不知道atob()btoa(),因此Buffer

ES6 -> ES5

const 只是 var .. 有点
(x, y) => {...} 只是 function(x, y) {...}
const [login, password] = ...split() 只是两个 var 作业合二为一

source of inspiration (uses packages)


以上是一个超级简单示例,旨在超级短并快速部署到您的游乐场服务器。但正如 cmets 中所指出的,密码也可以包含冒号字符:。要从 b64auth 中正确提取它,您可以使用它。
  // parse login and password from headers
  const b64auth = (req.headers.authorization || '').split(' ')[1] || ''
  const strauth = Buffer.from(b64auth, 'base64').toString()
  const splitIndex = strauth.indexOf(':')
  const login = strauth.substring(0, splitIndex)
  const password = strauth.substring(splitIndex + 1)

  // using shorter regex by @adabru
  // const [_, login, password] = strauth.match(/(.*?):(.*)/) || []

一个语句中的基本身份验证

...另一方面,如果您只使用一个或很少的登录名,这是您需要的最低要求:(您甚至根本不需要解析凭据)

function (req, res) {
//btoa('yourlogin:yourpassword') -> "eW91cmxvZ2luOnlvdXJwYXNzd29yZA=="
//btoa('otherlogin:otherpassword') -> "b3RoZXJsb2dpbjpvdGhlcnBhc3N3b3Jk"

  // Verify credentials
  if (  req.headers.authorization !== 'Basic eW91cmxvZ2luOnlvdXJwYXNzd29yZA=='
     && req.headers.authorization !== 'Basic b3RoZXJsb2dpbjpvdGhlcnBhc3N3b3Jk')        
    return res.status(401).send('Authentication required.') // Access denied.   

  // Access granted...
  res.send('hello world')
  // or call next() if you use it as middleware (as snippet #1)
}

PS:您是否需要同时拥有“安全”和“公共”路径?考虑改用express.router

var securedRoutes = require('express').Router()

securedRoutes.use(/* auth-middleware from above */)
securedRoutes.get('path1', /* ... */) 

app.use('/secure', securedRoutes)
app.get('public', /* ... */)

// example.com/public       // no-auth
// example.com/secure/path1 // requires auth

【讨论】:

  • 不要使用.split(':'),因为它会阻塞至少包含一个冒号的密码。此类密码根据RFC 2617有效。
  • 冒号部分也可以使用正则表达式const [_, login, password] = strauth.match(/(.*?):(.*)/) || []
  • 使用!== 比较密码会使您容易受到定时攻击。 en.wikipedia.org/wiki/Timing_attack 确保使用恒定时间字符串比较。
  • 使用Buffer.from() // for stringsBuffer.alloc() // for numbers 因为Buffer() 因安全问题而被弃用。
  • @Qwerty 我没有一篇关于该主题的好文章(维基百科有帮助吗?请参阅@hraban 分享的en.wikipedia.org/wiki/Timing_attack)。您的观点是正确的——网络因素使定时攻击不太实用。但依赖此将是一个“通过默默无闻的安全”的案例——它可能会在这里和那里有所帮助,但它不如实现恒定时间比较那样提供保护。请参阅nvd.nist.gov/vuln/detail/CVE-2015-7576,了解 Rails 中的一个类似漏洞,以及它的严重程度如何评定。
【解决方案6】:

Express 已删除此功能,现在建议您使用 basic-auth 库。

这里是一个如何使用的例子:

var http = require('http')
var auth = require('basic-auth')

// Create server
var server = http.createServer(function (req, res) {
  var credentials = auth(req)

  if (!credentials || credentials.name !== 'aladdin' || credentials.pass !== 'opensesame') {
    res.statusCode = 401
    res.setHeader('WWW-Authenticate', 'Basic realm="example"')
    res.end('Access denied')
  } else {
    res.end('Access granted')
  }
})

// Listen
server.listen(3000)

要向此路由发送请求,您需要包含格式为基本身份验证的Authorization header

首先发送 curl 请求,您必须采用 name:passbase64 编码,或者在这种情况下 aladdin:opensesame 等于 YWxhZGRpbjpvcGVuc2VzYW1l

您的 curl 请求将如下所示:

 curl -H "Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l" http://localhost:3000/

【讨论】:

    【解决方案7】:

    我们可以在不需要任何模块的情况下实现基本授权

    //1.
    var http = require('http');
    
    //2.
    var credentials = {
        userName: "vikas kohli",
        password: "vikas123"
    };
    var realm = 'Basic Authentication';
    
    //3.
    function authenticationStatus(resp) {
        resp.writeHead(401, { 'WWW-Authenticate': 'Basic realm="' + realm + '"' });
        resp.end('Authorization is needed');
    
    };
    
    //4.
    var server = http.createServer(function (request, response) {
        var authentication, loginInfo;
    
        //5.
        if (!request.headers.authorization) {
            authenticationStatus (response);
            return;
        }
    
        //6.
        authentication = request.headers.authorization.replace(/^Basic/, '');
    
        //7.
        authentication = (new Buffer(authentication, 'base64')).toString('utf8');
    
        //8.
        loginInfo = authentication.split(':');
    
        //9.
        if (loginInfo[0] === credentials.userName && loginInfo[1] === credentials.password) {
            response.end('Great You are Authenticated...');
             // now you call url by commenting the above line and pass the next() function
        }else{
    
        authenticationStatus (response);
    
    }
    
    });
     server.listen(5050);
    

    来源:-http://www.dotnetcurry.com/nodejs/1231/basic-authentication-using-nodejs

    【讨论】:

      【解决方案8】:

      TL;DR:

      express.basicAuth 消失了
      basic-auth-connect 已弃用
      basic-auth 没有任何逻辑
      http-auth 是矫枉过正
      express-basic-auth 是什么你想要的

      更多信息:

      既然您使用的是 Express,那么您可以使用 express-basic-auth 中间件。

      查看文档:

      例子:

      const app = require('express')();
      const basicAuth = require('express-basic-auth');
       
      app.use(basicAuth({
          users: { admin: 'supersecret123' },
          challenge: true // <--- needed to actually show the login dialog!
      }));
      

      【讨论】:

      • 花了我一段时间才弄清楚challenge: true 选项
      • @VitaliiZurian 好点 - 我将它添加到答案中。感谢您指出。
      • @rsp 你知道如何仅将其应用于特定路线吗?
      • 如果不想添加其他依赖的话,在一行上手写basic auth就很简单了……
      • 客户端 url 会是什么样子?
      【解决方案9】:
      function auth (req, res, next) {
        console.log(req.headers);
        var authHeader = req.headers.authorization;
        if (!authHeader) {
            var err = new Error('You are not authenticated!');
            res.setHeader('WWW-Authenticate', 'Basic');
            err.status = 401;
            next(err);
            return;
        }
        var auth = new Buffer.from(authHeader.split(' ')[1], 'base64').toString().split(':');
        var user = auth[0];
        var pass = auth[1];
        if (user == 'admin' && pass == 'password') {
            next(); // authorized
        } else {
            var err = new Error('You are not authenticated!');
            res.setHeader('WWW-Authenticate', 'Basic');      
            err.status = 401;
            next(err);
        }
      }
      app.use(auth);
      

      【讨论】:

      • 希望它能解决问题,但请添加对代码的解释,以便用户完全理解他/她真正想要的。
      【解决方案10】:

      安装 express-basic-auth 依赖项:

       npm i express-basic-auth
      

      需要您在其中创建应用的身份验证包

      const app = require('express')();
      const basicAuth = require('express-basic-auth');
      

      并像这样设置中间件:

      app.use(basicAuth({
          users: { 'my-username': 'my-password' },
          challenge: true,
      }));
      

      【讨论】:

        猜你喜欢
        • 2014-12-01
        • 2018-03-20
        • 1970-01-01
        • 1970-01-01
        • 2015-09-07
        • 2017-05-25
        • 1970-01-01
        • 1970-01-01
        • 2012-09-08
        相关资源
        最近更新 更多